feat(ui-toolkit,governance): description preview and read more pattern (#4599)
Co-authored-by: Joe <joe@vega.xyz>
This commit is contained in:
parent
97f243e5f7
commit
52dea6d0dc
@ -1,44 +1,27 @@
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { RoundedWrapper } from '@vegaprotocol/ui-toolkit';
|
||||
import { SubHeading } from '../../../../components/heading';
|
||||
import { CollapsibleToggle } from '../../../../components/collapsible-toggle';
|
||||
import { RoundedWrapper, ShowMore } from '@vegaprotocol/ui-toolkit';
|
||||
|
||||
export const ProposalDescription = ({
|
||||
description,
|
||||
}: {
|
||||
description: string;
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const [showDescription, setShowDescription] = useState(false);
|
||||
|
||||
return (
|
||||
<section data-testid="proposal-description">
|
||||
<CollapsibleToggle
|
||||
toggleState={showDescription}
|
||||
setToggleState={setShowDescription}
|
||||
dataTestId={'proposal-description-toggle'}
|
||||
>
|
||||
<SubHeading title={t('proposalDescription')} />
|
||||
</CollapsibleToggle>
|
||||
|
||||
{showDescription && (
|
||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
||||
<div className="p-2">
|
||||
<ReactMarkdown
|
||||
className="react-markdown-container"
|
||||
/* Prevents HTML embedded in the description from rendering */
|
||||
skipHtml={true}
|
||||
/* Stops users embedding images which could be used for tracking */
|
||||
disallowedElements={['img']}
|
||||
linkTarget="_blank"
|
||||
>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</div>
|
||||
</RoundedWrapper>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
}) => (
|
||||
<section data-testid="proposal-description">
|
||||
<RoundedWrapper paddingBottom={true} marginBottomLarge={true}>
|
||||
<div className="p-2">
|
||||
<ShowMore>
|
||||
<ReactMarkdown
|
||||
className="react-markdown-container"
|
||||
/* Prevents HTML embedded in the description from rendering */
|
||||
skipHtml={true}
|
||||
/* Stops users embedding images which could be used for tracking */
|
||||
disallowedElements={['img']}
|
||||
linkTarget="_blank"
|
||||
>
|
||||
{description}
|
||||
</ReactMarkdown>
|
||||
</ShowMore>
|
||||
</div>
|
||||
</RoundedWrapper>
|
||||
</section>
|
||||
);
|
||||
|
@ -34,6 +34,7 @@ export * from './progress-bar';
|
||||
export * from './radio-group';
|
||||
export * from './rounded-wrapper';
|
||||
export * from './select';
|
||||
export * from './show-more';
|
||||
export * from './simple-grid';
|
||||
export * from './slider';
|
||||
export * from './sparkline';
|
||||
|
1
libs/ui-toolkit/src/components/show-more/index.ts
Normal file
1
libs/ui-toolkit/src/components/show-more/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export * from './show-more';
|
@ -0,0 +1,9 @@
|
||||
import { render } from '@testing-library/react';
|
||||
import { ShowMore } from './show-more';
|
||||
|
||||
describe('Button', () => {
|
||||
it('should render successfully', () => {
|
||||
const { baseElement } = render(<ShowMore>test</ShowMore>);
|
||||
expect(baseElement).toBeTruthy();
|
||||
});
|
||||
});
|
@ -0,0 +1,50 @@
|
||||
import type { Story, Meta } from '@storybook/react';
|
||||
import { ShowMore } from './show-more';
|
||||
|
||||
export default {
|
||||
component: ShowMore,
|
||||
title: 'ShowMore',
|
||||
} as Meta;
|
||||
|
||||
const Template: Story = (args) => (
|
||||
<ShowMore {...args}>
|
||||
<p>
|
||||
Spaceflight will never tolerate carelessness, incapacity, and neglect.
|
||||
Somewhere, somehow, we screwed up. It could have been in design, build, or
|
||||
test. Whatever it was, we should have caught it. We were too gung ho about
|
||||
the schedule and we locked out all of the problems we saw each day in our
|
||||
work. “Every element of the program was in trouble and so were we. The
|
||||
simulators were not working, Mission Control was behind in virtually every
|
||||
area, and the flight and test procedures changed daily. Nothing we did had
|
||||
any shelf life. Not one of us stood up and said, ‘Dammit, stop!’ I don’t
|
||||
know what Thompson’s committee will find as the cause, but I know what I
|
||||
find. We are the cause! We were not ready! We did not do our job. We were
|
||||
rolling the dice, hoping that things would come together by launch day,
|
||||
when in our hearts we knew it would take a miracle. We were pushing the
|
||||
schedule and betting that the Cape would slip before we did. “From this
|
||||
day forward, Flight Control will be known by two words: ‘Tough’ and
|
||||
‘Competent.’ Tough means we are forever accountable for what we do or what
|
||||
we fail to do. We will never again compromise our responsibilities. Every
|
||||
time we walk into Mission Control we will know what we stand for.
|
||||
Competent means we will never take anything for granted. We will never be
|
||||
found short in our knowledge and in our skills. Mission Control will be
|
||||
perfect. When you leave this meeting today you will go to your office and
|
||||
the first thing you will do there is to write ‘Tough and Competent’ on
|
||||
your blackboards. It will never be erased. Each day when you enter the
|
||||
room these words will remind you of the price paid by Grissom, White, and
|
||||
Chaffee. These words are the price of admission to the ranks of Mission
|
||||
Control.
|
||||
</p>
|
||||
</ShowMore>
|
||||
);
|
||||
|
||||
export const Default = Template.bind({});
|
||||
export const CustomMaxHeight = Template.bind({});
|
||||
CustomMaxHeight.args = {
|
||||
closedMaxHeightPx: 50,
|
||||
};
|
||||
|
||||
export const CustomOverlayColour = Template.bind({});
|
||||
CustomOverlayColour.args = {
|
||||
overlayColourOverrides: 'to-yellow-400',
|
||||
};
|
78
libs/ui-toolkit/src/components/show-more/show-more.tsx
Normal file
78
libs/ui-toolkit/src/components/show-more/show-more.tsx
Normal file
@ -0,0 +1,78 @@
|
||||
import classNames from 'classnames';
|
||||
import { useRef, useState, useEffect } from 'react';
|
||||
import { t } from '@vegaprotocol/i18n';
|
||||
import { Button } from '../button';
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
type ShowMoreProps = {
|
||||
children: ReactNode;
|
||||
closedMaxHeightPx?: number;
|
||||
overlayColourOverrides?: string;
|
||||
};
|
||||
|
||||
export const ShowMore = ({
|
||||
children,
|
||||
closedMaxHeightPx = 125,
|
||||
overlayColourOverrides,
|
||||
}: ShowMoreProps) => {
|
||||
const containerRef = useRef<HTMLDivElement | null>(null);
|
||||
const [expanded, setExpanded] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const checkHeight = () => {
|
||||
const container = containerRef.current;
|
||||
if (container) {
|
||||
container.scrollHeight < closedMaxHeightPx
|
||||
? setExpanded(true)
|
||||
: setExpanded(false);
|
||||
}
|
||||
};
|
||||
|
||||
checkHeight();
|
||||
|
||||
window.addEventListener('resize', checkHeight);
|
||||
|
||||
return () => {
|
||||
window.removeEventListener('resize', checkHeight);
|
||||
};
|
||||
}, [closedMaxHeightPx]);
|
||||
|
||||
const containerClasses = classNames(
|
||||
'overflow-hidden transition-all ease-in-out duration-300',
|
||||
{
|
||||
'max-h-none': expanded,
|
||||
}
|
||||
);
|
||||
|
||||
const overlayClasses = classNames(
|
||||
`absolute w-full h-16 bottom-0 left-0 transition-opacity duration-300 bg-gradient-to-b from-transparent ${
|
||||
overlayColourOverrides ? overlayColourOverrides : 'to-white dark:to-black'
|
||||
}`,
|
||||
{
|
||||
hidden: expanded,
|
||||
}
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="relative">
|
||||
<div
|
||||
ref={containerRef}
|
||||
className={containerClasses}
|
||||
style={{ maxHeight: expanded ? 'none' : `${closedMaxHeightPx}px` }}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
<div className={overlayClasses}></div>
|
||||
</div>
|
||||
|
||||
{!expanded && (
|
||||
<div className="mt-1 text-center">
|
||||
<Button size={'sm'} onClick={() => setExpanded(true)}>
|
||||
{t('Show more')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
Loading…
Reference in New Issue
Block a user