diff --git a/README.md b/README.md index 094bb889..1a50edc0 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ -## (2020-10-07) +## (2020-10-08) - chore: add code of conduct ([6859555](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/6859555)) - chore: add identicons ([45ddea1](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/45ddea1)) - chore: add issue templates ([6adcbee](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/6adcbee)) - chore: add more status icons ([3225e43](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/3225e43)) +- chore: add share buttons to appeals ([4e35099](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/4e35099)) - chore: add social icons and fix font rendering ([b8e09b5](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/b8e09b5)) - chore: auto-detect Web3 call type ([15ae304](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/15ae304)) - chore: begin work on profile details page ([e4283ef](https://github.com/Proof-Of-Humanity/proof-of-humanity-web/commit/e4283ef)) diff --git a/_pages/index/submission-filters.js b/_pages/index/submission-filters.js index 8c5fe136..10ab57e5 100644 --- a/_pages/index/submission-filters.js +++ b/_pages/index/submission-filters.js @@ -1,4 +1,4 @@ -import { Card, Input, Option, Select, Text } from "@kleros/components"; +import { Card, Input, Select, Text } from "@kleros/components"; import { Search } from "@kleros/icons"; import { useRouter } from "next/router"; @@ -10,9 +10,11 @@ export default function SubmissionFilters({ numberOfSubmissions }) { - {numberOfSubmissions} Profile - {Number(numberOfSubmissions) === 1 ? "" : "s"} + + {(numberOfSubmissions || numberOfSubmissions === 0) && + `${numberOfSubmissions} Profile${ + numberOfSubmissions === 1 ? "" : "s" + }`} } headerSx={{ fontWeight: "bold", justifyContent: "flex-end" }} @@ -34,26 +36,21 @@ export default function SubmissionFilters({ numberOfSubmissions }) { }} /> + value={submissionStatusEnum.array.find( + ({ kebabCase }) => kebabCase === router.query.status + )} + label="Filter by status:" + /> ); } diff --git a/_pages/profile/[id]/submission-details-card/deadlines/challenge-button.js b/_pages/profile/[id]/submission-details-card/deadlines/challenge-button.js index f72fffef..28b7b636 100644 --- a/_pages/profile/[id]/submission-details-card/deadlines/challenge-button.js +++ b/_pages/profile/[id]/submission-details-card/deadlines/challenge-button.js @@ -9,13 +9,15 @@ import { NextLink, Popup, Text, + ethereumAddressRegExp, useContract, useWeb3, + zeroAddress, } from "@kleros/components"; import { useEffect, useMemo, useState } from "react"; import { graphql, useFragment } from "relay-hooks"; -import { challengeReasonEnum, ethereumAddressRegExp, zeroAddress } from "data"; +import { challengeReasonEnum } from "data"; const challengeButtonFragments = { contract: graphql` diff --git a/_pages/profile/[id]/submission-details-card/deadlines/remove-button.js b/_pages/profile/[id]/submission-details-card/deadlines/remove-button.js index a6b31d9a..710c64ff 100644 --- a/_pages/profile/[id]/submission-details-card/deadlines/remove-button.js +++ b/_pages/profile/[id]/submission-details-card/deadlines/remove-button.js @@ -6,12 +6,11 @@ import { Text, useContract, useWeb3, + zeroAddress, } from "@kleros/components"; import { useMemo } from "react"; import { graphql, useFragment } from "relay-hooks"; -import { zeroAddress } from "data"; - const removeButtonFragments = { contract: graphql` fragment removeButtonContract on Contract { diff --git a/components/button.js b/components/button.js index 152f1232..9fe9a2be 100644 --- a/components/button.js +++ b/components/button.js @@ -32,9 +32,9 @@ const Button = forwardRef( return `linear-gradient(90deg, ${primary} 0%, ${secondary} 100%)`; }, position: "relative", - "&:focus": { + ":focus": { boxShadow({ colors: { text } }) { - return `0 0 2px ${text}`; + return `0 0 1px ${text}`; }, }, ...sx, @@ -47,10 +47,10 @@ const Button = forwardRef( diff --git a/components/index.js b/components/index.js index e64daea0..fbf2fa68 100644 --- a/components/index.js +++ b/components/index.js @@ -29,7 +29,7 @@ export { default as List, ListItem } from "./list"; export { default as Popup } from "./popup"; export { default as RelayProvider, useQuery } from "./relay-provider"; export { default as SVG } from "./svg"; -export { default as Select, Option } from "./select"; +export { default as Select } from "./select"; export { default as SocialIcons } from "./social-icons"; export { default as Text } from "./text"; export { default as Textarea } from "./textarea"; @@ -40,3 +40,4 @@ export { default as VotingHistory } from "./voting-history"; export { default as Web3Provider, useWeb3, useContract } from "./web3-provider"; export { NextLink, NextETHLink, createWrapConnection } from "./next-router"; +export { zeroAddress, ethereumAddressRegExp, createEnum } from "./parsing"; diff --git a/components/label.js b/components/label.js index 2a1c8e7f..6314d04a 100644 --- a/components/label.js +++ b/components/label.js @@ -1,5 +1,6 @@ +import { forwardRef } from "react"; import { Label as _Label } from "theme-ui"; -export default function Label(props) { - return <_Label {...props} />; -} +const Label = forwardRef((props, ref) => <_Label ref={ref} {...props} />); +Label.displayName = "Label"; +export default Label; diff --git a/components/list.js b/components/list.js index 4f5e40bb..adc050c5 100644 --- a/components/list.js +++ b/components/list.js @@ -1,9 +1,11 @@ +import { forwardRef } from "react"; import { Box } from "theme-ui"; -export default function List(props) { - return ; -} +const List = forwardRef((props, ref) => ); +List.displayName = "List"; +export default List; -export function ListItem(props) { - return ; -} +export const ListItem = forwardRef((props, ref) => ( + +)); +ListItem.displayName = "ListItem"; diff --git a/components/parsing.js b/components/parsing.js new file mode 100644 index 00000000..98f2a102 --- /dev/null +++ b/components/parsing.js @@ -0,0 +1,51 @@ +import lodashKebabCase from "lodash.kebabcase"; +import lodashStartCase from "lodash.startcase"; + +export const zeroAddress = "0x0000000000000000000000000000000000000000"; + +export const ethereumAddressRegExp = /^0x[\dA-Fa-f]{40}$/; + +export const createEnum = (keys, parse) => { + const _enum = keys.reduce( + (acc, key, index) => { + let extra; + if (Array.isArray(key)) [key, extra] = key; + + const value = { + key, + index, + camelCase: key[0].toLowerCase() + key.slice(1), + kebabCase: lodashKebabCase(key), + startCase: lodashStartCase(key), + ...extra, + }; + value.color = value.camelCase; + value.toString = () => value.startCase; + + acc[key] = value; + acc[index] = value; + acc[value.camelCase] = value; + acc[value.kebabCase] = value; + acc[value.startCase] = value; + return acc; + }, + { + parse: + parse || + ((arrayOrKey) => + Array.isArray(arrayOrKey) + ? arrayOrKey.reduce((acc, key) => { + const value = _enum[key]; + acc[key] = value; + acc[value.index] = value; + acc[value.camelCase] = value; + acc[value.kebabCase] = value; + acc[value.startCase] = value; + return acc; + }, {}) + : _enum[arrayOrKey]), + } + ); + _enum.array = keys.map((_, index) => _enum[index]); + return _enum; +}; diff --git a/components/select.js b/components/select.js index 4f40591c..1397faaa 100644 --- a/components/select.js +++ b/components/select.js @@ -1,9 +1,143 @@ -import { Box, Select as _Select } from "theme-ui"; +import { alpha } from "@theme-ui/color"; +import { useSelect } from "downshift"; +import { useRef } from "react"; +import { usePopper } from "react-popper"; +import { Box } from "theme-ui"; -export default function Select(props) { - return <_Select {...props} />; +import Button from "./button"; +import Label from "./label"; +import List, { ListItem } from "./list"; + +const popperOptions = { + modifiers: [ + { + name: "sameWidth", + enabled: true, + phase: "beforeWrite", + fn({ state }) { + state.styles.popper.width = `${state.rects.reference.width}px`; + }, + requires: ["computeStyles"], + effect: ({ state }) => { + state.elements.popper.style.width = `${state.elements.reference.offsetWidth}px`; + }, + }, + { + name: "offset", + options: { + offset: [0, 8], + }, + }, + { + name: "preventOverflow", + }, + ], +}; +function Icon({ item }) { + return item.Icon ? ( + + ) : null; } +export default function Select({ items, onChange, value, label, ...rest }) { + const { + getItemProps, + getLabelProps, + getMenuProps, + getToggleButtonProps, + highlightedIndex, + isOpen, + selectedItem, + } = useSelect({ + items, + onSelectedItemChange({ selectedItem: _selectedItem }) { + onChange(_selectedItem); + }, + initialSelectedItem: value, + }); + + const toggleButtonRef = useRef(); + const menuRef = useRef(); + const { + styles: { popper: popperStyle }, + attributes: { popper: popperAttributes }, + } = usePopper(toggleButtonRef.current, menuRef.current, popperOptions); -export function Option(props) { - return ; + const border = isOpen ? "borderBottom" : "borderTop"; + return ( + + + + + {isOpen && + items.map((item, index) => ( + + + {String(item)} + + ))} + + + ); } diff --git a/components/theme-provider.js b/components/theme-provider.js index b4fb6d2a..64245a9e 100644 --- a/components/theme-provider.js +++ b/components/theme-provider.js @@ -103,14 +103,14 @@ const theme = merge(merge(base, toTheme(typographyTheme)), { fontSize: 1, paddingX: 2, paddingY: 1, - "&:disabled:not([data-loading=true])": { + ":disabled:not([data-loading=true])": { backgroundColor: "skeleton", backgroundImage: "none !important", }, - "&:hover": { + ":hover": { opacity: 0.8, }, - "&:focus": { + ":focus": { outline: "none", }, spinner: { @@ -125,11 +125,28 @@ const theme = merge(merge(base, toTheme(typographyTheme)), { backgroundImage: "none !important", fontSize: 1, padding: 1, - "&:disabled:not([data-loading=true])": { + ":disabled:not([data-loading=true])": { backgroundColor: "skeleton", backgroundImage: "none !important", }, }, + select: { + backgroundColor: "background", + backgroundImage: "none !important", + borderColor: "skeleton", + borderStyle: "solid", + borderWidth: 1, + color: "text", + paddingLeft: 2, + paddingRight: 3, + paddingY: 1, + ":hover": { + opacity: 0.8, + }, + ":focus": { + outline: "none", + }, + }, }, cards: { primary: { @@ -137,7 +154,7 @@ const theme = merge(merge(base, toTheme(typographyTheme)), { boxShadow: "0 6px 10px rgba(255, 153, 0, 0.25)", fontFamily: "heading", fontSize: 0, - "&:hover:not([disabled])": { + ":hover:not([disabled])": { boxShadow: "0 6px 20px rgba(255, 153, 0, 0.25)", }, }, @@ -149,13 +166,13 @@ const theme = merge(merge(base, toTheme(typographyTheme)), { borderWidth: 2, fontSize: 1, padding: 2, - "&:disabled": { + ":disabled": { backgroundColor: "skeleton", }, - "&:focus": { + ":focus": { outline: "none", }, - "&:focus,&:hover:not([disabled]),&.active": { + ":focus,:hover:not([disabled]),&.active": { borderColor: "primary", }, }, @@ -205,13 +222,18 @@ const theme = merge(merge(base, toTheme(typographyTheme)), { input: { marginTop: 1, }, + visuallyHidden: { + border: 0, + clip: "rect(0 0 0 0)", + height: "1px", + margin: "-1px", + overflow: "hidden", + padding: 0, + position: "absolute", + width: "1px", + }, }, mutedInput: { border: "none" }, - select: { - borderColor: "skeleton", - paddingRight: 3, - paddingY: 1, - }, smallInput: { borderColor: "skeleton", fontSize: 1, padding: 1 }, textarea: { borderColor: "skeleton" }, }, @@ -241,6 +263,19 @@ const theme = merge(merge(base, toTheme(typographyTheme)), { textDecoration: "none", }, }, + select: { + list: { + backgroundColor: "background", + borderRadius: 3, + listStyle: "none", + padding: 0, + ":focus": { outline: "none" }, + item: { + paddingX: 2, + paddingY: 1, + }, + }, + }, tabs: { tabList: { fontSize: 1, @@ -262,6 +297,18 @@ const theme = merge(merge(base, toTheme(typographyTheme)), { }, }, text: { + buttons: { + primary: { + justifyContent: "space-evenly", + }, + secondary: { + justifyContent: "space-evenly", + }, + select: { + justifyContent: "flex-start", + paddingRight: 1, + }, + }, clipped: { overflow: "hidden", textOverflow: "ellipsis", diff --git a/components/voting-history.js b/components/voting-history.js index 589807fe..1b76a254 100644 --- a/components/voting-history.js +++ b/components/voting-history.js @@ -3,7 +3,7 @@ import { Box, Flex } from "theme-ui"; import { createUseDataloaders } from "./archon-provider"; import Card from "./card"; -import Select, { Option } from "./select"; +import Select from "./select"; import Tabs, { Tab, TabList, TabPanel } from "./tabs"; import { useWeb3 } from "./web3-provider"; @@ -96,27 +96,19 @@ function VotingHistoryTabPanel({ <> + label="Choose a round:" + /> {rulingDescriptions && ( + items={rulingDescriptions} + onChange={(value) => setRuling(rulingDescriptions.indexOf(value))} + value={rulingDescriptions[ruling]} + label="Choose a voting option:" + /> )} diff --git a/data/index.js b/data/index.js index 319c93fd..969a366c 100644 --- a/data/index.js +++ b/data/index.js @@ -1,9 +1,7 @@ export { challengeReasonEnum, - ethereumAddressRegExp, partyEnum, queryEnums, submissionStatusEnum, - zeroAddress, } from "./parsing"; export { useEvidenceFile } from "./use-dataloaders"; diff --git a/data/parsing.js b/data/parsing.js index 97d562f6..e634ea28 100644 --- a/data/parsing.js +++ b/data/parsing.js @@ -1,54 +1,5 @@ +import { createEnum } from "@kleros/components"; import { Check, Pending, X } from "@kleros/icons"; -import lodashKebabCase from "lodash.kebabcase"; -import lodashStartCase from "lodash.startcase"; - -export const zeroAddress = "0x0000000000000000000000000000000000000000"; - -export const ethereumAddressRegExp = /^0x[\dA-Fa-f]{40}$/; - -const createEnum = (keys, parse) => { - const _enum = keys.reduce( - (acc, key, index) => { - let extra; - if (Array.isArray(key)) [key, extra] = key; - - const value = { - key, - index, - camelCase: key[0].toLowerCase() + key.slice(1), - kebabCase: lodashKebabCase(key), - startCase: lodashStartCase(key), - ...extra, - }; - acc[key] = value; - acc[index] = value; - acc[value.camelCase] = value; - acc[value.kebabCase] = value; - acc[value.startCase] = value; - return acc; - }, - { - parse: - parse || - ((arrayOrKey) => - Array.isArray(arrayOrKey) - ? arrayOrKey.reduce((acc, key) => { - const value = _enum[key]; - acc[key] = value; - acc[value.index] = value; - acc[value.camelCase] = value; - acc[value.kebabCase] = value; - acc[value.startCase] = value; - return acc; - }, {}) - : _enum[arrayOrKey]), - } - ); - _enum.map = (callback) => keys.map((_, index) => callback(_enum[index])); - _enum.find = (callback) => - Object.values(_enum).find((value) => callback(value)); - return _enum; -}; export const submissionStatusEnum = createEnum( [ diff --git a/package-lock.json b/package-lock.json index 81647679..4d3c070b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2887,6 +2887,11 @@ "fastq": "^1.6.0" } }, + "@popperjs/core": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.5.3.tgz", + "integrity": "sha512-RFwCobxsvZ6j7twS7dHIZQZituMIDJJNHS/qY6iuthVebxS3zhRY+jaC2roEKiAYaVuTcGmX6Luc6YBcf6zJVg==" + }, "@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", @@ -3037,6 +3042,15 @@ "defer-to-connect": "^1.0.1" } }, + "@theme-ui/color": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@theme-ui/color/-/color-0.3.1.tgz", + "integrity": "sha512-Bgew6etspp087AdrKuMpcWjIQDQ8UTp28wtHCXgi1Ip5ClFn9i91DvaKgqXk9UwQJFL/IwydX8Bks/+u9tf0WA==", + "requires": { + "@theme-ui/css": "^0.3.1", + "polished": "^3.4.1" + } + }, "@theme-ui/color-modes": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/@theme-ui/color-modes/-/color-modes-0.3.1.tgz", @@ -5708,6 +5722,11 @@ "arity-n": "^1.0.4" } }, + "compute-scroll-into-view": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-1.0.16.tgz", + "integrity": "sha512-a85LHKY81oQnikatZYA90pufpZ6sQx++BoCxOEMsjpZx+ZnaKGQnCyCehTRr/1p9GBIAHTjcU9k71kSYWloLiQ==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -7431,6 +7450,27 @@ "minimatch": "^3.0.4" } }, + "downshift": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/downshift/-/downshift-6.0.6.tgz", + "integrity": "sha512-tmLab3cXCn6PtZYl9V8r/nB2m+7/nCNrwo0B3kTHo/2lRBHr+1en1VNOQt2wIt0ajanAnxquZ00WPCyxe6cNFQ==", + "requires": { + "@babel/runtime": "^7.11.2", + "compute-scroll-into-view": "^1.0.14", + "prop-types": "^15.7.2", + "react-is": "^16.13.1" + }, + "dependencies": { + "@babel/runtime": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", + "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + } + } + }, "drbg.js": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/drbg.js/-/drbg.js-1.0.1.tgz", @@ -15965,6 +16005,14 @@ "ts-pnp": "^1.1.6" } }, + "polished": { + "version": "3.6.7", + "resolved": "https://registry.npmjs.org/polished/-/polished-3.6.7.tgz", + "integrity": "sha512-b4OViUOihwV0icb9PHmWbR+vPqaSzSAEbgLskvb7ANPATVXGiYv/TQFHQo65S53WU9i5EQ1I03YDOJW7K0bmYg==", + "requires": { + "@babel/runtime": "^7.9.2" + } + }, "posix-character-classes": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", @@ -17154,6 +17202,15 @@ "react-fast-compare": "^3.0.1" } }, + "react-popper": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-popper/-/react-popper-2.2.3.tgz", + "integrity": "sha512-mOEiMNT1249js0jJvkrOjyHsGvqcJd3aGW/agkiMoZk3bZ1fXN1wQszIQSjHIai48fE67+zwF8Cs+C4fWqlfjw==", + "requires": { + "react-fast-compare": "^3.0.1", + "warning": "^4.0.2" + } + }, "react-refresh": { "version": "0.8.3", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.8.3.tgz", @@ -20190,6 +20247,14 @@ "he": "^1.1.0" } }, + "warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "requires": { + "loose-envify": "^1.0.0" + } + }, "watchpack": { "version": "2.0.0-beta.13", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.0.0-beta.13.tgz", diff --git a/package.json b/package.json index 8d3b4cda..9fd2b4b8 100644 --- a/package.json +++ b/package.json @@ -38,6 +38,8 @@ "dependencies": { "@emotion/core": "^10.0.35", "@kleros/archon": "^0.10.4", + "@popperjs/core": "^2.5.3", + "@theme-ui/color": "^0.3.1", "@theme-ui/presets": "^0.3.0", "@theme-ui/typography": "^0.3.0", "@unilogin/provider": "^0.6.1", @@ -45,6 +47,7 @@ "authereum": "0.0.4-beta.186", "buffer": "^5.6.0", "dataloader": "^2.0.0", + "downshift": "^6.0.6", "formik": "^2.1.5", "lodash.kebabcase": "^4.1.1", "lodash.startcase": "^4.4.0", @@ -58,6 +61,7 @@ "react-jazzicon": "^0.1.3", "react-loading-skeleton": "^2.1.1", "react-player": "^2.6.1", + "react-popper": "^2.2.3", "react-relay-network-modern": "^4.7.4", "react-ripples": "^2.2.1", "react-scroll-to": "^3.0.0-beta.6",