Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: APP-3094 - Update ProposalDataListItemStrucure component #147

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7d900b9
show calendar avatarIcon only when date is given
Fabricevladimir Apr 18, 2024
3c0f670
convert protocol update to optional tag
Fabricevladimir Apr 18, 2024
00101e6
add stage properties to approval threshold result
Fabricevladimir Apr 18, 2024
c0b939f
add stage properties to mjv results
Fabricevladimir Apr 18, 2024
76abf0a
add multi-body story
Fabricevladimir Apr 18, 2024
cf5938d
update approved by typo
Fabricevladimir Apr 18, 2024
3ad59ce
add line-clamp and update internationalization comment
Fabricevladimir Apr 18, 2024
69dab96
make result optional
Fabricevladimir Apr 18, 2024
2ffcf44
allow for multiple publishers
Fabricevladimir Apr 19, 2024
d459883
update changelog
Fabricevladimir Apr 19, 2024
e47b7de
update changelog
Fabricevladimir Apr 19, 2024
fb45cea
add key and update tests
Fabricevladimir Apr 19, 2024
87519de
remove separate variable for the separator
Fabricevladimir Apr 19, 2024
a4b8865
add optional proposal id
Fabricevladimir Apr 22, 2024
3dc6184
truncate link component children on overflow
Fabricevladimir Apr 22, 2024
fb698ef
update proposalDataListItemStructure stories
Fabricevladimir Apr 22, 2024
ad74163
update tests for approvalThresholdResult
Fabricevladimir Apr 22, 2024
cf8b174
update tests for majorityVotingResult
Fabricevladimir Apr 22, 2024
d8530ad
update tests for proposalDataListItemStructure
Fabricevladimir Apr 22, 2024
70e6485
add test to show 3+ creators when more than 3 proposal publishers
Fabricevladimir Apr 22, 2024
26d2af7
update proposalDatListItemStructure props
Fabricevladimir Apr 23, 2024
530f6b8
adapt proposalDataListItemStructure and corresponding tests and stories
Fabricevladimir Apr 23, 2024
e759c52
update approvalThresholdResult and corresponding tests
Fabricevladimir Apr 23, 2024
32c411d
update majorityVotingResult and corresponding tests
Fabricevladimir Apr 23, 2024
bc47ef6
update proposalDataListItemStatus tests
Fabricevladimir Apr 23, 2024
c57f262
remove any props not required in ProposalDataListItemStructure tests
Fabricevladimir Apr 23, 2024
a0acead
add isAddressEqual function to addressUtils
Fabricevladimir Apr 23, 2024
5268a2f
update proposalDataListItemStructure to use isAddressEqual utility
Fabricevladimir Apr 23, 2024
6a25a35
update proposalDataListItemStructure tests with zeroAddress as stub a…
Fabricevladimir Apr 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,20 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Added

- Implement `ProposalDataListItemSkeleton` module component
- Extend `addressUtils` with `isAddressEqual` method

### Changed

- Remove padding from `DataListContainer`, `DataListFilterStatus`, `DataListPagination` and `DataListRoot`
- Add `stageId` and `stageTitle` properties to `IApprovalThresholdResult` & `IMajorityVotingResult` interfaces
- Add `id` and optional `tag` properties to `ProposalDataListItemStructure`
- Remove `publisherProfileLink` and `protocolUpdate` properties from `ProposalDataListItemStructure`
- Update `date` and `result` properties of `ProposalDataListItemStructure` to be optional and `publisher` to allow for
multiple publishers

### Fixed

- `Link` core component to truncate on overflow

## [1.0.23] - 2024-04-18

Expand Down
4 changes: 2 additions & 2 deletions src/core/components/link/link.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,8 @@ export const Link = React.forwardRef<HTMLAnchorElement, ILinkProps>((props, ref)
{...(disabled && { tabIndex: -1, 'aria-disabled': 'true' })}
{...otherProps}
>
<div className="flex items-center gap-x-2 truncate">
{children}
<div className="flex items-center gap-x-2">
<span className="truncate">{children}</span>
{iconRight && <Icon icon={iconRight} size="sm" />}
</div>
{description && <p className={descriptionClassName}>{description}</p>}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,22 @@ describe('<ApprovalThresholdResult /> component', () => {
expect(screen.getByText(expectedApproval)).toBeInTheDocument();
expect(screen.getByText(expectedThreshold)).toBeInTheDocument();
});

it('renders the stage title and stage id when provided', () => {
const stage = {
title: 'Test Stage',
id: '3',
};

render(createTestComponent({ stage }));

expect(screen.getByText(stage.title)).toBeInTheDocument();
expect(screen.getByText(stage.id)).toBeInTheDocument();
});

it('renders the default stage title when not provided', () => {
render(createTestComponent());

expect(screen.getByText(/approved by/i)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ export interface IApprovalThresholdResultProps extends IApprovalThresholdResult
* `ApprovalThresholdResult` component
*/
export const ApprovalThresholdResult: React.FC<IApprovalThresholdResultProps> = (props) => {
const { approvalAmount, approvalThreshold } = props;
const { approvalAmount, approvalThreshold, stage } = props;
const percentage = approvalThreshold !== 0 ? (approvalAmount / approvalThreshold) * 100 : 100;

return (
// TODO: apply internationalization to Approved By, of, and Members [APP-2627]
// TODO: apply internationalization to Approved By, of, Stage, and Members [APP-2627]
<div className="flex w-full flex-col gap-y-2 rounded-xl border border-neutral-100 bg-neutral-0 px-4 py-3 shadow-neutral-sm md:gap-y-3 md:px-6 md:py-5">
<div className="flex flex-1 gap-x-4 leading-tight text-neutral-800 md:gap-x-6 md:text-lg">
<span className="flex-1">Approved By</span>
<div className="flex flex-1 gap-x-3 leading-tight text-neutral-800 md:gap-x-6 md:text-lg">
<span className="line-clamp-1 flex-1">{stage?.title ?? 'Approved By'}</span>
{stage?.id != null && (
<span className="flex shrink-0 justify-between gap-x-0.5">
<span className="flex-1 text-neutral-500">Stage</span>
{stage.id}
</span>
)}
</div>
<Progress value={percentage} />
<div className="flex gap-x-0.5 leading-tight text-neutral-500 md:gap-x-1 md:text-lg">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,22 @@ describe('<MajorityVotingResult /> component', () => {
expect(screen.getByText(mockProps.votePercentage, { exact: false })).toBeInTheDocument();
expect(screen.getByRole('progressbar')).toBeInTheDocument();
});

it('renders the stage title and stage id when provided', () => {
const stage = {
title: 'Test Stage',
id: '3',
};

render(createTestComponent({ stage }));

expect(screen.getByText(stage.title)).toBeInTheDocument();
expect(screen.getByText(stage.id)).toBeInTheDocument();
});

it('renders the default stage title when not provided', () => {
render(createTestComponent());

expect(screen.getByText(/winning option/i)).toBeInTheDocument();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@ export interface IMajorityVotingResultProps extends IMajorityVotingResult {}
* `MajorityVotingResult` component
*/
export const MajorityVotingResult: React.FC<IMajorityVotingResultProps> = (props) => {
const { option, voteAmount, votePercentage } = props;
const { option, stage, voteAmount, votePercentage } = props;

return (
// TODO: apply internationalization to Winning Option [APP-2627]
// TODO: apply internationalization to Winning Option and Stage [APP-2627]
<div className="flex w-full flex-col gap-y-2 rounded-xl border border-neutral-100 bg-neutral-0 px-4 py-3 shadow-neutral-sm md:gap-y-3 md:px-6 md:py-5">
<div className="flex flex-1 gap-x-4 leading-tight text-neutral-800 md:gap-x-6 md:text-lg">
<span className="flex-1">Winning Option</span>
<span className="text-primary-400">{`${votePercentage}%`}</span>
<span className="line-clamp-1 flex-1">{stage?.title ?? 'Winning Option'}</span>
{stage?.id == null && <span className="text-primary-400">{`${votePercentage}%`}</span>}
{stage?.id != null && (
<span className="flex shrink-0 justify-between gap-x-0.5">
<span className="flex-1 text-neutral-500">Stage</span>
{stage.id}
</span>
)}
</div>
<Progress value={votePercentage} />
<div className="flex gap-x-4 leading-tight md:gap-x-6 md:text-lg">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,14 @@ describe('<ProposalDataListItemStatus /> component', () => {
expect(screen.getByTestId(IconType.CALENDAR)).toBeInTheDocument();
});

it('does not render the calendar icon when date property is not defined', () => {
const status = 'accepted';

render(createTestComponent({ status, date: undefined }));

expect(screen.queryByTestId(IconType.CALENDAR)).not.toBeInTheDocument();
});

it("only displays the date for proposals with a status that is not 'draft'", () => {
const date = 'test date';
const status = 'draft';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,9 @@ export const ProposalDataListItemStatus: React.FC<IProposalDataListItemStatusPro
</span>
{ongoingAndVoted && <AvatarIcon icon={IconType.CHECKMARK} responsiveSize={{ md: 'md' }} />}
{ongoing && !voted && <StatePingAnimation variant={ongoingStatusToPingVariant[status]} />}
{!ongoing && !voted && <AvatarIcon icon={IconType.CALENDAR} responsiveSize={{ md: 'md' }} />}
{!ongoing && !voted && date && (
Fabricevladimir marked this conversation as resolved.
Show resolved Hide resolved
<AvatarIcon icon={IconType.CALENDAR} responsiveSize={{ md: 'md' }} />
)}
</div>
)}
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,25 +20,25 @@ export interface IProposalDataListItemStructureBaseProps<TType extends ProposalT
extends IDataListItemProps,
IWeb3ComponentProps {
/**
* Indicates date relative to the proposal status
* Proposal id
*/
date: string;
id?: string;
/**
* Indicates whether the proposal is a protocol update
* Indicates date relative to the proposal status
*/
protocolUpdate?: boolean;
date?: string;
/**
* Publisher address (and optional ENS name)
* Optional tag indicating proposal type
*/
publisher: ICompositeAddress;
tag?: string;
/**
* Link to the publisher's profile
* Publisher(s) address (and optional ENS name and profile link)
*/
publisherProfileLink: string;
publisher: IPublisher | IPublisher[];
/**
* Result of the proposal shown only when it is active, challenged or vetoed.
*/
result: TType extends 'majorityVoting' ? IMajorityVotingResult : IApprovalThresholdResult;
result?: TType extends 'majorityVoting' ? IMajorityVotingResult : IApprovalThresholdResult;
cgero-eth marked this conversation as resolved.
Show resolved Hide resolved
/**
* Proposal status
*/
Expand All @@ -60,8 +60,33 @@ export interface IProposalDataListItemStructureBaseProps<TType extends ProposalT
*/
voted?: boolean;
}
export interface IPublisher extends ICompositeAddress {
/**
* Link to additional information about the publisher, such as a profile page or block explorer.
*/
link?: string;
}

export interface IProposalStage {
/**
* Name of the proposal stage
*/
title?: string;

/**
* Id of the proposal stage
*/
id: string | number;
}

export interface IProposalResultBase {
/**
* Proposal stage
*/
stage?: IProposalStage;
}

export interface IApprovalThresholdResult {
export interface IApprovalThresholdResult extends IProposalResultBase {
/**
* Number of approvals for the proposal
*/
Expand All @@ -72,7 +97,7 @@ export interface IApprovalThresholdResult {
approvalThreshold: number;
}

export interface IMajorityVotingResult {
export interface IMajorityVotingResult extends IProposalResultBase {
/**
* Winning option
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,13 @@ const meta: Meta<typeof ProposalDataListItem.Structure> = {

type Story = StoryObj<typeof ProposalDataListItem.Structure>;

const baseArgs: Omit<IProposalDataListItemStructureProps, 'result'> = {
const basePublisher = {
address: '0xd5fb864ACfD6BB2f72939f122e89fF7F475924f5',
link: 'https://app.aragon.org/#/daos/base/0xd2705c56aa4edb98271cb8cea2b0df3288ad4585/members/0xd5fb864ACfD6BB2f72939f122e89fF7F475924f5',
};

const baseArgs: Omit<IProposalDataListItemStructureProps, 'result' | 'publisher'> = {
date: '5 days left',
protocolUpdate: false,
publisher: { address: '0xd5fb864ACfD6BB2f72939f122e89fF7F475924f5' },
publisherProfileLink:
'https://app.aragon.org/#/daos/base/0xd2705c56aa4edb98271cb8cea2b0df3288ad4585/members/0xd5fb864ACfD6BB2f72939f122e89fF7F475924f5',
status: 'draft',
title: 'This is a very serious proposal to send funds to a wallet address',
summary: `Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris vel eleifend neque, in mattis eros.
Expand All @@ -39,6 +40,7 @@ const baseArgs: Omit<IProposalDataListItemStructureProps, 'result'> = {
export const MajorityVoting: Story = {
args: {
...baseArgs,
publisher: { ...basePublisher },
type: 'majorityVoting',
result: {
option: 'yes',
Expand All @@ -61,7 +63,7 @@ export const MajorityVoting: Story = {
export const ApprovalThreshold: Story = {
args: {
...baseArgs,
publisher: { name: 'sio.eth', address: baseArgs.publisher.address },
publisher: { ...basePublisher, name: 'sio.eth' },
type: 'approvalThreshold',
result: {
approvalAmount: 4,
Expand All @@ -77,4 +79,33 @@ export const ApprovalThreshold: Story = {
),
};

/**
* Example of the `ProposalDataListItem.Structure` module component for a multi-body proposal.
*/
export const MultiBody: Story = {
args: {
...baseArgs,
id: 'PIP-1',
publisher: [
{ ...basePublisher, name: '0xRugg', link: undefined },
{ ...basePublisher, name: 'Bob the Builder', link: undefined },
{ ...basePublisher, name: 'sio.eth' },
{ ...basePublisher },
],
type: 'approvalThreshold',
result: {
stage: { title: 'Founders Approval Council', id: '1' },
approvalAmount: 4,
approvalThreshold: 6,
},
},
render: (props) => (
<DataList.Root entityLabel="Proposals">
<DataList.Container SkeletonElement={ProposalDataListItem.Skeleton}>
<ProposalDataListItem.Structure {...props} />
</DataList.Container>
</DataList.Root>
),
};

export default meta;
Loading
Loading