From f1555aacb440ae9a26f8fb7e9e4da7a6c3487923 Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Sun, 3 Feb 2019 18:37:05 +0100 Subject: [PATCH 01/22] Turn checkboxes to checkbox inputs --- components/ChecklistItem/index.js | 13 ++++-- components/ChecklistItem/style.js | 78 ++++++++++++++++++++----------- 2 files changed, 60 insertions(+), 31 deletions(-) diff --git a/components/ChecklistItem/index.js b/components/ChecklistItem/index.js index c9fbeb1..e4975e0 100644 --- a/components/ChecklistItem/index.js +++ b/components/ChecklistItem/index.js @@ -10,7 +10,6 @@ import { Resources } from './Resources'; import { Container, CheckboxContainer, - Checkbox, CardContent, ResourceContent, Divider, @@ -59,8 +58,16 @@ class ChecklistItem extends React.Component { - - + + + diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js index 4861b9d..5d576fe 100644 --- a/components/ChecklistItem/style.js +++ b/components/ChecklistItem/style.js @@ -69,35 +69,29 @@ export const CheckboxContainer = styled.div` margin-top: 4px; width: 32px; } -`; -export const Checkbox = styled.span` - width: 32px; - height: 32px; - border-radius: 4px; - border: 1px solid - ${props => (props.isChecked ? theme.bg.default : theme.border.default)}; - background: ${props => - props.isChecked ? theme.success.default : theme.bg.wash}; - cursor: pointer; - position: relative; - background-image: ${props => - props.isChecked - ? 'radial-gradient(circle at top right, #a913de, #6ac9ff)' - : 'none'}; - box-shadow: ${props => - props.isChecked ? 'inset 0 0 1px rgba(0,0,0,0.4)' : 'none'}; - - &:hover { - ${props => !props.isChecked && Shadows.default}; - background: ${theme.bg.default}; - background-image: ${props => - props.isChecked - ? 'radial-gradient(circle at top right, #a913de, #6ac9ff)' - : 'none'}; + input[type="checkbox"] { + position: absolute; } - &:after { + input[type="checkbox"] + label { + width: 32px; + height: 32px; + border-radius: 4px; + border: 1px solid ${theme.border.default}; + background: ${theme.bg.wash}; + cursor: pointer; + position: relative; + overflow: hidden; + text-indent: -1000px; + } + + input[type="checkbox"] + label:hover { + ${Shadows.default}; + background: ${theme.bg.default}; + } + + input[type="checkbox"] + label::after { content: ''; position: absolute; display: block; @@ -105,11 +99,39 @@ export const Checkbox = styled.span` top: 6px; width: 6px; height: 12px; - border: solid - ${props => (props.isChecked ? theme.bg.default : theme.border.active)}; + border: solid ${theme.border.active}; border-width: 0 2px 2px 0; transform: rotate(45deg); } + + input[type="checkbox"]:checked + label { + border: 1px solid ${theme.bg.default}; + } + + input[type="checkbox"]:checked + label::after { + border: solid ${theme.bg.default}; + border-width: 0 2px 2px 0; + } + + /* This ::before pseudo-element is used to animate the gradient + which does not support transitions. */ + input[type="checkbox"] + label::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + transition: opacity ${theme.animations.default}; + opacity: 0; + background-image: radial-gradient(circle at top right, #a913de, #6ac9ff); + box-shadow: inset 0 0 1px rgba(0,0,0,0.4); + } + + input[type="checkbox"]:checked + label::before { + opacity: 1; + background-color: ${theme.success.default}; + } `; export const ResourceContent = styled.div` From 94dd42a5669b88f38792c91f82b6b58f6f9d29e5 Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Sun, 3 Feb 2019 18:44:22 +0100 Subject: [PATCH 02/22] Add ARIA attributes --- components/ChecklistItem/index.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/components/ChecklistItem/index.js b/components/ChecklistItem/index.js index e4975e0..5c5ea42 100644 --- a/components/ChecklistItem/index.js +++ b/components/ChecklistItem/index.js @@ -64,13 +64,19 @@ class ChecklistItem extends React.Component { checked={isChecked} id={`checkbox_${resource.id}`} onChange={this.handleSetChecked} + aria-controls={`content_${resource.id}`} /> - + Date: Wed, 6 Feb 2019 10:29:35 +0100 Subject: [PATCH 03/22] Accessibility: share buttons should be elements only, not nested buttons #96 --- components/Button/style.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/Button/style.js b/components/Button/style.js index 37dbe8a..7ea0786 100644 --- a/components/Button/style.js +++ b/components/Button/style.js @@ -225,7 +225,7 @@ export const ButtonSegmentRow = styled.div` } `; -export const FacebookButton = styled.button` +export const FacebookButton = styled.span` ${base} border: 1px solid ${theme.social.facebook}; color: ${theme.bg.default}; @@ -266,7 +266,7 @@ export const FacebookButton = styled.button` } `; -export const TwitterButton = styled.button` +export const TwitterButton = styled.span` ${base} border: 1px solid ${theme.social.twitter}; color: ${theme.bg.default}; From a8336bc5ceb8a23b5f3d630da5d86908ddd82654 Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Wed, 6 Feb 2019 10:32:05 +0100 Subject: [PATCH 04/22] Move main app link to app name + icon to avoid nesting inside --- components/ChecklistItem/App.js | 6 +++--- components/ChecklistItem/style.js | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/components/ChecklistItem/App.js b/components/ChecklistItem/App.js index 6b378d1..395f4a1 100644 --- a/components/ChecklistItem/App.js +++ b/components/ChecklistItem/App.js @@ -38,8 +38,8 @@ export const AppRow = ({ app }: Props) => { }; return ( - - + + {app.image && } {app.name} @@ -49,4 +49,4 @@ export const AppRow = ({ app }: Props) => { {app.offer && } ); -}; +}; \ No newline at end of file diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js index 5d576fe..74123c8 100644 --- a/components/ChecklistItem/style.js +++ b/components/ChecklistItem/style.js @@ -151,12 +151,12 @@ export const ResourceContent = styled.div` export const AppsContainer = styled.div``; -export const AppMeta = styled.div` +export const AppMeta = styled.a` display: flex; align-items: center; `; -export const AppRowContainer = styled.a` +export const AppRowContainer = styled.div` display: flex; justify-content: space-between; align-items: center; From 66fabbb197586521f60415213cea321bb88a6413 Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Wed, 6 Feb 2019 10:32:38 +0100 Subject: [PATCH 05/22] Horizontal rule divs are now hr --- components/ChecklistItem/style.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js index 74123c8..3515486 100644 --- a/components/ChecklistItem/style.js +++ b/components/ChecklistItem/style.js @@ -331,7 +331,7 @@ export const ResourceName = styled.p` font-weight: 400; `; -export const Divider = styled.div` +export const Divider = styled.hr` border-bottom: 1px solid ${tint(theme.bg.wash, -4)}; margin-top: 24px; margin-bottom: 24px; From 33cdf24e38dbab9520dbf2549834b55cf1905258 Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Wed, 6 Feb 2019 12:34:38 +0100 Subject: [PATCH 06/22] Better :focus and :active styles for buttons and links --- components/Button/style.js | 28 +++++++------ components/ChecklistItem/style.js | 70 +++++++++++++++++++++++++------ components/Footer/style.js | 8 +++- components/Header/index.js | 29 +++++++------ components/Header/style.js | 25 +++++++++-- components/Page/style.js | 9 ++-- components/ShareButtons/index.js | 12 +++--- 7 files changed, 129 insertions(+), 52 deletions(-) diff --git a/components/Button/style.js b/components/Button/style.js index 7ea0786..3d61ecc 100644 --- a/components/Button/style.js +++ b/components/Button/style.js @@ -58,11 +58,15 @@ const base = css` cursor: not-allowed; } - &:hover { + &:hover, &:active, &:focus { transition: all 0.2s ease-in-out; box-shadow: ${props => props.disabled ? 'none' : `${theme.shadows.button}`}; } + &:active, &:focus { + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.5)}; + } `; export const Button = styled.button` @@ -126,7 +130,7 @@ export const PrimaryButton = styled.button` &:focus { box-shadow: 0 0 0 1px ${props => props.theme.bg.default}, 0 0 0 3px ${props => - hexa(props.theme.brand.alt, 0.16)}; + hexa(props.theme.brand.alt, 0.5)}; } `; @@ -137,15 +141,15 @@ export const GhostButton = styled.button` background-color: transparent; background-image: none; - &:hover { - background: ${props => tint(props.theme.bg.wash, -3)}; + &:hover, &:active, &:focus { + background: ${props => tint(props.theme.bg.wash, -8)}; color: ${theme.text.default}; box-shadow: none; } - &:focus { + &:active, &:focus { box-shadow: 0 0 0 1px ${theme.bg.default}, - 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.08)}; + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; } `; @@ -220,12 +224,12 @@ export const ButtonSegmentRow = styled.div` ${PrimaryButton} { &:focus { box-shadow: 0 0 0 1px ${theme.bg.default}, - 0 0 0 3px ${props => hexa(props.theme.brand.alt, 0.16)}; + 0 0 0 3px ${props => hexa(props.theme.brand.alt, 0.5)}; } } `; -export const FacebookButton = styled.span` +export const FacebookButton = styled.a` ${base} border: 1px solid ${theme.social.facebook}; color: ${theme.bg.default}; @@ -262,11 +266,11 @@ export const FacebookButton = styled.span` &:focus { box-shadow: 0 0 0 1px ${props => props.theme.bg.default}, 0 0 0 3px ${props => - hexa(props.theme.social.facebook, 0.16)}; + hexa(props.theme.social.facebook, 0.5)}; } `; -export const TwitterButton = styled.span` +export const TwitterButton = styled.a` ${base} border: 1px solid ${theme.social.twitter}; color: ${theme.bg.default}; @@ -303,7 +307,7 @@ export const TwitterButton = styled.span` &:focus { box-shadow: 0 0 0 1px ${props => props.theme.bg.default}, 0 0 0 3px ${props => - hexa(props.theme.social.twitter, 0.16)}; + hexa(props.theme.social.twitter, 0.5)}; } `; @@ -357,7 +361,7 @@ export const CopyLinkButton = styled.button` box-shadow: 0 0 0 1px ${props => props.theme.bg.default}, 0 0 0 3px ${props => props.isClicked - ? hexa(props.theme.success.default, 0.16) + ? hexa(props.theme.success.default, 0.5) : props.theme.border.default}; } `; diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js index 3515486..0a6f4f2 100644 --- a/components/ChecklistItem/style.js +++ b/components/ChecklistItem/style.js @@ -2,7 +2,7 @@ import styled, { css } from 'styled-components'; import Markdown from 'react-markdown'; import { theme } from '../theme'; -import { Shadows, tint } from '../globals'; +import { Shadows, tint, hexa } from '../globals'; export const Container = styled.div` margin-bottom: 24px; @@ -132,6 +132,16 @@ export const CheckboxContainer = styled.div` opacity: 1; background-color: ${theme.success.default}; } + + input[type="checkbox"]:active + label, input[type="checkbox"]:focus + label { + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.brand.default, 0.5)}; + } + input[type="checkbox"]:active:checked + label, input[type="checkbox"]:focus:checked + label { + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.spectrum.default, 0.5)}; + } + `; export const ResourceContent = styled.div` @@ -151,11 +161,6 @@ export const ResourceContent = styled.div` export const AppsContainer = styled.div``; -export const AppMeta = styled.a` - display: flex; - align-items: center; -`; - export const AppRowContainer = styled.div` display: flex; justify-content: space-between; @@ -192,6 +197,19 @@ export const AppRowContainer = styled.div` } `; +export const AppMeta = styled.a` + display: flex; + align-items: center; + padding-right: 6px; + border-radius: 8px; + + &:active, &:focus { + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; + background: ${theme.bg.wash}; + } +`; + export const AppIcon = styled.img` width: 40px; height: 40px; @@ -229,8 +247,7 @@ export const AppSourcesListItem = styled.li` align-items: center; justify-content: center; color: ${theme.text.tertiary}; - padding: 4px 10px; - border-radius: 4px; + padding: 2px 8px; min-width: 56px; transition: all 0.1s ease-in-out; @@ -239,6 +256,19 @@ export const AppSourcesListItem = styled.li` flex-direction: column; align-items: center; justify-content: center; + padding: 2px; + border-radius: 8px; + + &:hover { + color: ${theme.text.default}; + } + + &:active, &:focus { + color: ${theme.text.default}; + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; + background: ${theme.bg.wash}; + } } .icon { @@ -247,9 +277,6 @@ export const AppSourcesListItem = styled.li` left: 4px; } - &:hover { - color: ${theme.text.default}; - } @media (max-width: 768px) { padding: 4px; @@ -302,11 +329,16 @@ export const ResourceRowContainer = styled.a` margin-right: 8px; } - &:hover { + &:hover, &:active, &:focus { background: ${theme.bg.wash}; color: ${theme.text.default}; } + &:active, &:focus { + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; + } + @media (max-width: 768px) { width: calc(100% + 40px); border-radius: 0; @@ -314,7 +346,7 @@ export const ResourceRowContainer = styled.a` margin-left: -24px; padding-left: 24px; - &:hover { + &:hover, &:active, &:focus { background: ${theme.bg.default}!important; } @@ -366,6 +398,12 @@ export const Uncollapse = styled.span` color: ${theme.text.default}; background: ${tint(theme.bg.wash, -4)}; } + &:active, &:focus { + transition: all 0.2s ease-in-out; + + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; + } `; export const OfferContainer = styled.a` @@ -401,6 +439,12 @@ export const OfferContainer = styled.a` &:hover { color: ${theme.text.secondary}; } + &:active, &:focus { + box-shadow: inset 0 0 1px ${theme.border.active}, + 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; + background: ${theme.bg.wash}; + } `; export const LeftBorder = styled.div` diff --git a/components/Footer/style.js b/components/Footer/style.js index ea3a1e5..399e9f1 100644 --- a/components/Footer/style.js +++ b/components/Footer/style.js @@ -1,6 +1,7 @@ // @flow import styled from 'styled-components'; import { theme } from '../theme'; +import { hexa } from '../globals'; export const Container = styled.div` margin-top: 128px; @@ -34,10 +35,15 @@ export const Icons = styled.div` color: ${theme.text.tertiary}; } - a:hover { + a:hover, a:active, a:focus { color: ${theme.text.default}; } + a:active, a:focus { + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; + } + .icon { margin-left: 16px; } diff --git a/components/Header/index.js b/components/Header/index.js index 03b31df..3552c43 100644 --- a/components/Header/index.js +++ b/components/Header/index.js @@ -1,7 +1,7 @@ // @flow import * as React from 'react'; import Link from 'next/link'; -import { Container, ButtonRowContainer, Label } from './style'; +import { Container, ButtonRowContainer, Label, LogoLink } from './style'; import { PrimaryButton, GhostButton } from '../Button'; import Logo from './Logo'; @@ -14,27 +14,30 @@ export default function Header(props: Props) { return ( - - - - - - +
+ + + + + + +
- - About - + + About + - - Contribute - + Contribute +
); diff --git a/components/Header/style.js b/components/Header/style.js index 4ad6b1e..8492fb2 100644 --- a/components/Header/style.js +++ b/components/Header/style.js @@ -1,6 +1,7 @@ // @flow import styled from 'styled-components'; import { theme } from '../theme'; +import { hexa } from '../globals'; export const Container = styled.div` display: grid; @@ -18,10 +19,6 @@ export const Container = styled.div` props.showHeaderShadow ? '0 4px 8px rgba(0,0,0,0.04)' : 'none'}; transition: all 0.2s ease-in-out; - a { - margin-left: 8px; - } - @media (max-width: 968px) { padding: 8px 16px; grid-template-columns: 1fr 1fr; @@ -41,6 +38,26 @@ export const ButtonRowContainer = styled.div` justify-content: flex-end; grid-area: actions; align-items: center; + + a { + margin-left: 8px; + } +`; + +export const LogoLink = styled.a` + transition: all ${props => props.theme.animations.default}; + display: inline-flex; + align-items: center; + border-radius: 6px; + + &:hover { + transform: scale(1.2); + } + &:active, &:focus { + transform: scale(1.2); + box-shadow: 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.text.tertiary, 0.25)}; + } `; export const Label = styled.h1` diff --git a/components/Page/style.js b/components/Page/style.js index 20b4973..64859f9 100644 --- a/components/Page/style.js +++ b/components/Page/style.js @@ -1,6 +1,6 @@ // @flow import styled from 'styled-components'; -import { tint } from '../globals'; +import { hexa, tint } from '../globals'; import { theme } from '../theme'; export const Container = styled.div` @@ -113,8 +113,11 @@ export const ScrollToTop = styled.button` transition: all 0.2s ease-in-out; } - &:active { - box-shadow: 0 6px 20px rgba(0, 0, 0, 0.09); + &:active, &:focus { + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.09), + 0 0 0 1px ${theme.bg.default}, + 0 0 0 3px ${props => hexa(props.theme.brand.default, 0.5)}; + outline: none; transform: translateY(-2px); transition: all 0.2s ease-in-out; } diff --git a/components/ShareButtons/index.js b/components/ShareButtons/index.js index 76109f2..832d339 100644 --- a/components/ShareButtons/index.js +++ b/components/ShareButtons/index.js @@ -6,21 +6,21 @@ import { Container } from './style'; export default function ShareButtons() { return ( - - Share - + Share + - - Tweet - + Tweet + Share Link From 968cc1b51a43cf38737d4796b82d96f8167966cf Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Wed, 6 Feb 2019 12:36:11 +0100 Subject: [PATCH 07/22] Complete redo of the display toggle for main card content --- components/ChecklistItem/Heading.js | 9 ++++--- components/ChecklistItem/index.js | 39 +++++++++++++++++++++++------ components/ChecklistItem/style.js | 22 +++++++++++++++- 3 files changed, 59 insertions(+), 11 deletions(-) diff --git a/components/ChecklistItem/Heading.js b/components/ChecklistItem/Heading.js index 77c9fb5..94122cd 100644 --- a/components/ChecklistItem/Heading.js +++ b/components/ChecklistItem/Heading.js @@ -1,6 +1,6 @@ // @flow import React from 'react'; -import { Title, Description, Uncollapse } from './style'; +import { Title, Uncollapse } from './style'; import type { ChecklistResource } from '../../types'; type Props = { @@ -13,10 +13,13 @@ export const Heading = ({ resource, isCollapsed, handleCollapse }: Props) => ( {resource.title} - <Uncollapse onClick={handleCollapse}> + <Uncollapse + onClick={handleCollapse} + aria-controls={`content_${resource.id}`} + aria-expanded={!isCollapsed} + > {isCollapsed ? 'Show details' : 'Hide details'} </Uncollapse> - {!isCollapsed && } ); diff --git a/components/ChecklistItem/index.js b/components/ChecklistItem/index.js index 5c5ea42..53c1cc8 100644 --- a/components/ChecklistItem/index.js +++ b/components/ChecklistItem/index.js @@ -13,6 +13,8 @@ import { CardContent, ResourceContent, Divider, + Description, + Content, } from './style'; type Props = { @@ -23,10 +25,17 @@ type State = { isChecked: boolean, isLoading: boolean, isCollapsed: boolean, + contentHeight: number, }; class ChecklistItem extends React.Component { - state = { isChecked: false, isLoading: true, isCollapsed: true }; + state = { isChecked: false, isLoading: true, isCollapsed: true, contentHeight: 2000, }; + + constructor(props) { + super(props); + + this.contentContainer = React.createRef(); + } componentDidMount() { const { resource } = this.props; @@ -38,6 +47,15 @@ class ChecklistItem extends React.Component { }); } + componentDidUpdate(prevProps, prevState) { + + if (prevState.isLoading && !this.state.isLoading) { + return this.setState({ + contentHeight: this.contentContainer.current.scrollHeight, + }) + } + } + handleSetChecked = () => { const { isChecked } = this.state; const { resource } = this.props; @@ -45,11 +63,19 @@ class ChecklistItem extends React.Component { return this.setState({ isChecked: !isChecked, isCollapsed: !isChecked }); }; - uncollapse = () => - this.setState(state => ({ isCollapsed: !state.isCollapsed })); + uncollapse = () => { + this.setState(state => ({ isCollapsed: !state.isCollapsed })); + this.contentContainer.current.focus(); + }; + + handleAppsExpand = appsContainerHeight => { + return this.setState({ + contentHeight: this.contentContainer.current.scrollHeight + appsContainerHeight, + }) + } render() { - const { isChecked, isLoading, isCollapsed } = this.state; + const { isChecked, isLoading, isCollapsed, contentHeight } = this.state; const { resource } = this.props; if (isLoading) return ; @@ -60,13 +86,14 @@ class ChecklistItem extends React.Component { - @@ -74,8 +101,6 @@ class ChecklistItem extends React.Component { Date: Wed, 6 Feb 2019 12:36:58 +0100 Subject: [PATCH 08/22] Complete redo of display toggle for App expanded content --- components/ChecklistItem/Apps.js | 97 ++++++++++++++++++++++--------- components/ChecklistItem/index.js | 41 +++++++++---- components/ChecklistItem/style.js | 21 +++++++ 3 files changed, 120 insertions(+), 39 deletions(-) diff --git a/components/ChecklistItem/Apps.js b/components/ChecklistItem/Apps.js index e0a18e0..3872f39 100644 --- a/components/ChecklistItem/Apps.js +++ b/components/ChecklistItem/Apps.js @@ -1,43 +1,86 @@ // @flow // $FlowIssue -import React, { useState } from 'react'; -import { AppsContainer, SectionHeading, ExpandContainer } from './style'; +import React from 'react'; +import { AppsContainer, SectionHeading, ExpandContainer, ExpandContent } from './style'; import { Button } from '../Button'; import type { ChecklistResource } from '../../types'; import { AppRow } from './App'; type Props = { resource: ChecklistResource, + handleAppsExpand: Function, }; -export const Apps = ({ resource }: Props) => { - const [overflowExpanded, setOverflowExpanded] = useState(false); +type State = { + overflowExpanded: boolean, + contentHeight: number, +}; - if (!resource.apps) return null; +export class Apps extends React.Component { + state = { overflowExpanded: false, contentHeight: 2000, }; + + constructor(props) { + super(props) + if (!this.props.resource.apps) return null; + + this.expandContentContainer = React.createRef(); - let appList = resource.apps; - let overflowAppList; - if (appList.length > 3) { - overflowAppList = appList.slice(3, appList.length); - appList = appList.slice(0, 3); } - return ( - - Apps - {appList.map(app => ( - - ))} + handleExpand = () => { + let expandContentHeight = this.expandContentContainer.current.scrollHeight; - {overflowAppList && !overflowExpanded && ( - setOverflowExpanded(true)}> - - - )} + this.setState({ + contentHeight: expandContentHeight, + overflowExpanded: !this.state.overflowExpanded, + }) + + this.props.handleAppsExpand(this.state.overflowExpanded ? -expandContentHeight : expandContentHeight); + } + + render() { + let appList = this.props.resource.apps; + let overflowAppList; + if (appList.length > 3) { + overflowAppList = appList.slice(3, appList.length); + appList = appList.slice(0, 3); + } + + const { overflowExpanded, contentHeight } = this.state; + const { resource } = this.props; + + return ( + + Apps + {appList.map(app => ( + + ))} + + {overflowAppList && ( + + + {overflowAppList.map(app => )} + + + + + + + )} + + ); + } +} - {overflowAppList && - overflowExpanded && - overflowAppList.map(app => )} - - ); -}; diff --git a/components/ChecklistItem/index.js b/components/ChecklistItem/index.js index 53c1cc8..6aeaea4 100644 --- a/components/ChecklistItem/index.js +++ b/components/ChecklistItem/index.js @@ -108,19 +108,36 @@ class ChecklistItem extends React.Component { handleCollapse={this.uncollapse} /> - {!isCollapsed && resource.apps && ( - - - - - )} + + + + + {resource.apps && ( + + + + + )} + + {resource.resources && ( + + + + + )} + + - {!isCollapsed && resource.resources && ( - - - - - )} diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js index 63170e3..efc2216 100644 --- a/components/ChecklistItem/style.js +++ b/components/ChecklistItem/style.js @@ -503,3 +503,24 @@ export const ExpandContainer = styled.div` width: calc(100% + 40px); } `; + + +export const ExpandContent = styled.div` + transition: + max-height ${theme.animations.default}, + opacity ${theme.animations.default}, + visibility ${theme.animations.default}; + + &[aria-hidden="true"] { + max-height: 0; + opacity: 0; + visibility: hidden; + } + + &[aria-hidden="false"] { + max-height: unset; + max-height: var(--maxHeight); + opacity: 1; + visibility: visible; + } +` \ No newline at end of file From 64c6df6cbbc9738f0737e9739231aad67306d53e Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Wed, 6 Feb 2019 14:05:23 +0100 Subject: [PATCH 09/22] Better handling of default (non-hidden) state --- components/ChecklistItem/style.js | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/components/ChecklistItem/style.js b/components/ChecklistItem/style.js index efc2216..f15df18 100644 --- a/components/ChecklistItem/style.js +++ b/components/ChecklistItem/style.js @@ -381,18 +381,18 @@ export const Content = styled.div` opacity ${theme.animations.default}, visibility ${theme.animations.default}; + max-height: 2000px; + max-height: var(--maxHeight); + opacity: 1; + visibility: visible; + + &[aria-hidden="true"] { max-height: 0; opacity: 0; visibility: hidden; } - &[aria-hidden="false"] { - max-height: 2000px; - max-height: var(--maxHeight); - opacity: 1; - visibility: visible; - } ` export const Uncollapse = styled.button` @@ -510,6 +510,10 @@ export const ExpandContent = styled.div` max-height ${theme.animations.default}, opacity ${theme.animations.default}, visibility ${theme.animations.default}; + max-height: 2000px; + max-height: var(--maxHeight); + opacity: 1; + visibility: visible; &[aria-hidden="true"] { max-height: 0; @@ -517,10 +521,4 @@ export const ExpandContent = styled.div` visibility: hidden; } - &[aria-hidden="false"] { - max-height: unset; - max-height: var(--maxHeight); - opacity: 1; - visibility: visible; - } ` \ No newline at end of file From 8f6793348e9f363a2cf4fdea3eb8d0849f6c48e6 Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Wed, 6 Feb 2019 15:01:46 +0100 Subject: [PATCH 10/22] CopyLinkButton: no more a/button imbrication --- components/Button/CopyLinkButton.js | 29 ++++++++++------------------- components/Button/style.js | 18 +++++++++++++----- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/components/Button/CopyLinkButton.js b/components/Button/CopyLinkButton.js index 63bdc5c..94a75f4 100644 --- a/components/Button/CopyLinkButton.js +++ b/components/Button/CopyLinkButton.js @@ -1,16 +1,10 @@ // @flow // $FlowIssue import React, { useState } from 'react'; -import dynamic from 'next/dynamic'; -import * as Styled from './style'; +import { CopyLinkButton as StyledCopyLinkButton } from './style'; import Icon from '../Icon'; import type { ButtonProps } from './types'; -const Clipboard = dynamic(() => import('react-clipboard.js'), { - ssr: false, - loading: () => null, -}); - type CopyLinkProps = { ...$Exact, text: string, @@ -26,20 +20,17 @@ export default function CopyLinkButton(props: CopyLinkProps) { }; return ( - - - - {isClicked ? 'Copied!' : children} - - + + {isClicked ? 'Copied!' : children} + ); } diff --git a/components/Button/style.js b/components/Button/style.js index 3d61ecc..a5ad562 100644 --- a/components/Button/style.js +++ b/components/Button/style.js @@ -3,6 +3,12 @@ import styled, { css } from 'styled-components'; import { hexa, tint } from '../globals'; import type { ButtonSize } from './types'; import { theme } from '../theme'; +import dynamic from 'next/dynamic'; + +const Clipboard = dynamic(() => import('react-clipboard.js'), { + ssr: false, + loading: () => null, +}); const getPadding = (size: ButtonSize) => { switch (size) { @@ -32,7 +38,7 @@ const getFontSize = (size: ButtonSize) => { } }; -const base = css` +export const base = css` -webkit-appearance: none; display: flex; flex: none; @@ -311,8 +317,9 @@ export const TwitterButton = styled.a` } `; -export const CopyLinkButton = styled.button` +export const CopyLinkButton = styled(Clipboard)` ${base} + transition: all ${theme.animations.default}; border: 1px solid ${props => props.isClicked ? tint(props.theme.success.default, -10) @@ -360,8 +367,9 @@ export const CopyLinkButton = styled.button` &:focus { box-shadow: 0 0 0 1px ${props => props.theme.bg.default}, 0 0 0 3px ${props => - props.isClicked - ? hexa(props.theme.success.default, 0.5) - : props.theme.border.default}; + props.isClicked + ? hexa(props.theme.success.default, 0.5) + : props.theme.border.default}; } `; + From 08c8589d6d4d66fe6a3589786655768691fbc7fa Mon Sep 17 00:00:00 2001 From: Joachim Robert Date: Wed, 6 Feb 2019 15:02:13 +0100 Subject: [PATCH 11/22] Add type="button" on buttons --- components/Button/CopyLinkButton.js | 1 + components/ChecklistItem/Apps.js | 2 +- components/Page/index.js | 6 +++++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/components/Button/CopyLinkButton.js b/components/Button/CopyLinkButton.js index 94a75f4..329c670 100644 --- a/components/Button/CopyLinkButton.js +++ b/components/Button/CopyLinkButton.js @@ -27,6 +27,7 @@ export default function CopyLinkButton(props: CopyLinkProps) { data-cy="copy-link-button" isClicked={isClicked} aria-label="Copy the website’s address to your clipboard." + type="button" {...props} > diff --git a/components/ChecklistItem/Apps.js b/components/ChecklistItem/Apps.js index 3872f39..834a337 100644 --- a/components/ChecklistItem/Apps.js +++ b/components/ChecklistItem/Apps.js @@ -75,7 +75,7 @@ export class Apps extends React.Component { aria-expanded={overflowExpanded} aria-controls={`apps_${resource.id}`} > - + )} diff --git a/components/Page/index.js b/components/Page/index.js index c3075cc..13fddb7 100644 --- a/components/Page/index.js +++ b/components/Page/index.js @@ -74,7 +74,11 @@ export default function Page(props: Props) {
{children}