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

initial reusable component docs #189

Merged
merged 4 commits into from
Jan 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,18 @@ This is the repository of the BOS components for the NEAR DevHub trustees dashbo

Please refer to [NEAR DevHub](https://github.com/NEAR-DevHub/neardevhub-bos/blob/main/CONTRIBUTING.md) for general contribution guidelines. Details specific for this repository will be added later.

## Running tests

This project has many [playwright-tests](./playwright-tests/), which covers most of the functionality.

Some of the tests depend on the NEAR sandbox. The NEAR sandbox is a local blockchain for testing, and allows simulating the interaction with smart contracts.

To build the sandbox type the following:

`npm run build:sandbox`

Then you can run the tests using `npm run test` or `npm run test:watch:codespaces`. You can also add flags such as `--ui` for UI mode, or inspect the test configuration in [playwright.config.js](./playwright.config.js).

## Creating test videos

Creating videos of your automated tests is a great way of showcasing the changes in your contribution. Video recording of your test can be enabled in the [playwright.config.js](./playwright.config.js) under the `use` section where there is the `video` property that you should set to `on` ( Remember to NOT commit it with the `on` setting, as we don't want to waste github action resources on recording videos).
Expand Down
41 changes: 41 additions & 0 deletions docs/developer/ui-component-library.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
UI component library
====================

To ensure a consistent look and feel across the application, developers should reuse standard components, and avoid applying specific styling on pages.

# Implementing components

An example of a reusable component is the [Modal](../../instances/treasury-devdao.near/widget/lib/modal.jsx).


To implement a reusable component, we can create a function that takes `children` as a parameter. Here's an example of the `ModalFooter` component:

```jsx
const ModalFooter = ({ children }) =>
<div className="modalfooter d-flex gap-2 align-items-center justify-content-end mt-2">
{children}
</div>
;
```

We can then use it in another widget like this:

```jsx
const { Modal, ModalContent, ModalHeader, ModalFooter } = VM.require("${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.modal");

return <Modal>
<ModalHeader>
Modal title
</ModalHeader>
<ModalContent>
<p>
The modal message
</p>
</ModalContent>
<ModalFooter>
<button>Dismiss</button>
</ModalFooter>
</Modal>;
```

This way, we can have custom jsx inside the resuable component. The styles and classes of the reusable component will be applied to the custom jsx content.
33 changes: 22 additions & 11 deletions instances/treasury-devdao.near/widget/lib/modal.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const Modal = styled.div`
const ModalDiv = styled.div`
display: ${({ hidden }) => (hidden ? "none" : "flex")};
position: fixed;
inset: 0;
Expand Down Expand Up @@ -54,22 +54,14 @@ const ModalDialog = styled.div`
}
`;

const ModalHeader = styled.div`
const ModalHeaderDiv = styled.div`
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
padding-bottom: 4px;
`;

const ModalFooter = styled.div`
padding-top: 4px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: items-center;
`;

const CloseButton = styled.button`
display: flex;
align-items: center;
Expand Down Expand Up @@ -106,4 +98,23 @@ const NoButton = styled.button`
box-shadow: none;
`;

return { Modal, ModalBackdrop, ModalContent, ModalDialog, ModalHeader };
const ModalHeader = ({ children }) => (
<ModalHeaderDiv>
<h5 className="mb-0">{children}</h5>
</ModalHeaderDiv>
);

const ModalFooter = ({ children }) => (
<div className="modalfooter d-flex gap-2 align-items-center justify-content-end mt-2">
{children}
</div>
);

const Modal = ({ children }) => (
<ModalDiv>
<ModalBackdrop />
<ModalDialog className="card">{children}</ModalDialog>
</ModalDiv>
);

return { Modal, ModalContent, ModalHeader, ModalFooter };
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@ if (!instance) {
}

const { treasuryDaoID } = VM.require(`${instance}/widget/config.data`);
const { Modal, ModalBackdrop, ModalContent, ModalDialog, ModalHeader } =
VM.require("${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.modal");
const { Modal, ModalContent, ModalHeader, ModalFooter } = VM.require(
"${REPL_BASE_DEPLOYMENT_ACCOUNT}/widget/lib.modal"
);

const daoPolicy = Near.view(treasuryDaoID, "get_policy", {});
const lastProposalId = Near.view(treasuryDaoID, "get_last_proposal_id");
Expand Down Expand Up @@ -350,145 +351,136 @@ return (

{showAffectedProposalsModal ? (
<Modal>
<ModalBackdrop />
<ModalDialog className="card">
<ModalHeader>
<h5 className="mb-0">
<i class="bi bi-exclamation-triangle text-warning"></i>
Impact of changing voting duration
</h5>
</ModalHeader>
<ModalContent>
<p>
You are about to update the voting duration. This will impact
existing requests.
</p>
<ul>
{otherPendingRequests.length > 0 ? (
<li>
<b>{otherPendingRequests.length} pending requests</b> will
now follow the new voting duration policy.
</li>
) : (
""
)}
{proposalsThatWillExpire.length > 0 ? (
<li>
<b>{proposalsThatWillExpire.length} active requests</b>{" "}
under the old voting duration will move to the "Archived"
tab and close for voting. These requests were created
outside the new voting period and are no longer considered
active.
</li>
) : (
""
)}
{proposalsThatWillBeActive.length > 0 ? (
<li>
<b>{proposalsThatWillBeActive.length} expired requests</b>{" "}
under the old voting duration will move back to the
"Pending Requests" tab and reopen for voting. These
requests were created within the new voting period and are
no longer considered expired.
</li>
) : (
""
)}
</ul>
{showImpactedRequests ? (
<>
<h4>Summary of changes</h4>
<table className="table table-sm">
<thead>
<tr className="text-grey">
<th>Id</th>
<th>Description</th>
<th>Submission date</th>
<th>Current expiry</th>
<th>New expiry</th>
</tr>
</thead>
<tbody>
{proposalsThatWillExpire.map((proposal) => (
<tr class="proposal-that-will-expire">
<td>{proposal.id}</td>
<td>{proposal.description}</td>
<td>
{new Date(proposal.submissionTimeMillis)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.currentExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.newExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
</tr>
))}
{proposalsThatWillBeActive.map((proposal) => (
<tr class="proposal-that-will-be-active">
<td>{proposal.id}</td>
<td>{proposal.description}</td>
<td>
{new Date(proposal.submissionTimeMillis)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.currentExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.newExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
</tr>
))}
</tbody>
</table>
</>
<ModalHeader>
<i class="bi bi-exclamation-triangle text-warning"></i>
Impact of changing voting duration
</ModalHeader>
<ModalContent>
<p>
You are about to update the voting duration. This will impact
existing requests.
</p>
<ul>
{otherPendingRequests.length > 0 ? (
<li>
<b>{otherPendingRequests.length} pending requests</b> will
now follow the new voting duration policy.
</li>
) : (
""
)}
{proposalsThatWillExpire.length > 0 ? (
<li>
<b>{proposalsThatWillExpire.length} active requests</b>{" "}
under the old voting duration will move to the "Archived"
tab and close for voting. These requests were created
outside the new voting period and are no longer considered
active.
</li>
) : (
""
)}
{proposalsThatWillBeActive.length > 0 ? (
<p>
If you do not want expired proposals to be open for voting
again, you may need to delete them.
</p>
<li>
<b>{proposalsThatWillBeActive.length} expired requests</b>{" "}
under the old voting duration will move back to the "Pending
Requests" tab and reopen for voting. These requests were
created within the new voting period and are no longer
considered expired.
</li>
) : (
""
)}
</ModalContent>
<div className="modalfooter d-flex gap-2 align-items-center justify-content-end mt-2">
<Widget
src={
"${REPL_DEVHUB}/widget/devhub.components.molecule.Button"
}
props={{
classNames: { root: "btn-outline shadow-none border-0" },
label: "Cancel",
onClick: cancelChangeRequest,
}}
/>
<Widget
src={
"${REPL_DEVHUB}/widget/devhub.components.molecule.Button"
}
props={{
classNames: { root: "theme-btn" },
label: "Yes, proceed",
onClick: submitChangeRequest,
}}
/>
</div>
</ModalDialog>
</ul>
{showImpactedRequests ? (
<>
<h4>Summary of changes</h4>
<table className="table table-sm">
<thead>
<tr className="text-grey">
<th>Id</th>
<th>Description</th>
<th>Submission date</th>
<th>Current expiry</th>
<th>New expiry</th>
</tr>
</thead>
<tbody>
{proposalsThatWillExpire.map((proposal) => (
<tr class="proposal-that-will-expire">
<td>{proposal.id}</td>
<td>{proposal.description}</td>
<td>
{new Date(proposal.submissionTimeMillis)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.currentExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.newExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
</tr>
))}
{proposalsThatWillBeActive.map((proposal) => (
<tr class="proposal-that-will-be-active">
<td>{proposal.id}</td>
<td>{proposal.description}</td>
<td>
{new Date(proposal.submissionTimeMillis)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.currentExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
<td>
{new Date(proposal.newExpiryTime)
.toJSON()
.substring(0, "yyyy-mm-dd".length)}
</td>
</tr>
))}
</tbody>
</table>
</>
) : (
""
)}
{proposalsThatWillBeActive.length > 0 ? (
<p>
If you do not want expired proposals to be open for voting
again, you may need to delete them.
</p>
) : (
""
)}
</ModalContent>
<ModalFooter>
<Widget
src={"${REPL_DEVHUB}/widget/devhub.components.molecule.Button"}
props={{
classNames: { root: "btn-outline shadow-none border-0" },
label: "Cancel",
onClick: cancelChangeRequest,
}}
/>
<Widget
src={"${REPL_DEVHUB}/widget/devhub.components.molecule.Button"}
props={{
classNames: { root: "theme-btn" },
label: "Yes, proceed",
onClick: submitChangeRequest,
}}
/>
</ModalFooter>
</Modal>
) : (
""
Expand Down
Loading
Loading