diff options
author | 2021-01-16 22:31:50 +0300 | |
---|---|---|
committer | 2021-01-16 22:35:16 +0300 | |
commit | d7c20ba6e8f0675ad2fcd4fb41419de005ae3979 (patch) | |
tree | 235c151e941623acd14b86b2cb7375742ef745c8 /src | |
parent | Minor Style Adjustments (diff) |
Rewrites Components For Accessibility
Makes major changes to the structuring of checkboxes, and ranges to be
more accessible to all users.
Signed-off-by: Hassan Abouelela <[email protected]>
Diffstat (limited to 'src')
-rw-r--r-- | src/commonStyles.tsx | 23 | ||||
-rw-r--r-- | src/components/InputTypes/Checkbox.tsx | 27 | ||||
-rw-r--r-- | src/components/InputTypes/Range.tsx | 64 | ||||
-rw-r--r-- | src/components/InputTypes/index.tsx | 2 | ||||
-rw-r--r-- | src/components/Question.tsx | 11 |
5 files changed, 57 insertions, 70 deletions
diff --git a/src/commonStyles.tsx b/src/commonStyles.tsx index 5053846..71bbd13 100644 --- a/src/commonStyles.tsx +++ b/src/commonStyles.tsx @@ -14,6 +14,24 @@ const unselectable = css` user-select: none; `; +const hiddenInput = css` + position: absolute; + opacity: 0; + height: 0; + width: 0; +`; + +const multiSelectInput = css` + display: inline-block; + position: relative; + + margin: 1rem 0.5rem 0 0; + border: whitesmoke 0.2rem solid; + + background-color: whitesmoke; + transition: background-color 300ms; +`; + const textInputs = css` display: inline-block; width: min(20rem, 90%); @@ -32,8 +50,11 @@ const textInputs = css` border-radius: 8px; `; + export { selectable, unselectable, - textInputs + hiddenInput, + multiSelectInput, + textInputs, }; diff --git a/src/components/InputTypes/Checkbox.tsx b/src/components/InputTypes/Checkbox.tsx index f63da31..3093caf 100644 --- a/src/components/InputTypes/Checkbox.tsx +++ b/src/components/InputTypes/Checkbox.tsx @@ -2,6 +2,7 @@ import { jsx, css } from "@emotion/react"; import React, { ChangeEvent } from "react"; import colors from "../../colors"; +import { multiSelectInput, hiddenInput } from "../../commonStyles"; interface CheckboxProps { index: number, @@ -10,36 +11,25 @@ interface CheckboxProps { } const generalStyles = css` + cursor: pointer; + label { - display: inline-block; - position: relative; - top: 0.3rem; - width: 1em; height: 1em; + top: 0.3rem; - margin: 1rem 0.5rem 0 0; - border: whitesmoke 0.2rem solid; border-radius: 25%; - - transition: background-color 300ms; + cursor: pointer; } .unselected { background-color: white; } - .unselected:hover { + .unselected:focus-within, :hover .unselected { background-color: lightgray; } - input { - position: absolute; - opacity: 0; - height: 0; - width: 0; - } - .checkmark { position: absolute; } @@ -65,9 +55,8 @@ const activeStyles = css` export default function Checkbox(props: CheckboxProps): JSX.Element { return ( <label css={[generalStyles, activeStyles]}> - <label className="unselected"> - <input type="checkbox" value={props.option} - name={`${("000" + props.index).slice(-4)}. ${props.option}`} onChange={props.handler}/> + <label className="unselected" css={multiSelectInput}> + <input type="checkbox" value={props.option} css={hiddenInput} name={`${("000" + props.index).slice(-4)}. ${props.option}`} onChange={props.handler}/> <span className="checkmark"/> </label> {props.option}<br/> diff --git a/src/components/InputTypes/Range.tsx b/src/components/InputTypes/Range.tsx index af46b05..e2f89f4 100644 --- a/src/components/InputTypes/Range.tsx +++ b/src/components/InputTypes/Range.tsx @@ -1,32 +1,13 @@ /** @jsx jsx */ import { jsx, css } from "@emotion/react"; -import React from "react"; +import React, { ChangeEvent } from "react"; import colors from "../../colors"; +import { hiddenInput, multiSelectInput } from "../../commonStyles"; interface RangeProps { question_id: string, options: Array<string>, - state_dict: Map<string, string | boolean | null> -} - -interface handler_props { - state_dict: Map<string, string | boolean | null>, - ref: React.RefObject<HTMLLabelElement> -} - -let last_selection: Element; -function handler(this: handler_props): void { - if (last_selection) { - last_selection.classList.toggle("selected"); - } - - const dot: Element = this.ref.current!.lastElementChild!; // eslint-disable-line - dot.classList.toggle("selected"); - - last_selection = dot; - - const value: string = this.ref.current!.textContent!; //eslint-disable-line - this.state_dict.set("value", value); + handler: (event: ChangeEvent<HTMLInputElement>) => void } const containerStyles = css` @@ -57,26 +38,25 @@ const optionStyles = css` transition: transform 300ms; `; -const rangeDotStyles = css` - .range_dot { - width: 0.8rem; - height: 0.8rem; +const selectorStyles = css` + cursor: pointer; + + div { + width: 1rem; + height: 1rem; + background-color: whitesmoke; - border: 0.2rem solid whitesmoke; border-radius: 50%; - - transition: background-color 300ms; + margin: 0 100% 0 0; } - .range_dot.selected { - background-color: ${colors.blurple}; + :hover div, :focus-within div { + background-color: lightgray; } - @media (max-width: 800px) { - .range_dot { - margin-bottom: 1.5rem; - } + input:checked+div { + background-color: ${colors.blurple}; } `; @@ -88,15 +68,15 @@ const sliderContainerStyles = css` position: absolute; z-index: -1; - top: 2rem; + top: 2.1rem; transition: all 300ms; @media (max-width: 800px) { width: 0.5rem; - height: 88%; + height: 80%; - left: 0.32rem; + left: 0.4rem; background: whitesmoke; } @@ -116,17 +96,17 @@ const sliderStyles = css` export default function Range(props: RangeProps): JSX.Element { const range = props.options.map((option, index) => { - const ref: React.RefObject<HTMLLabelElement> = React.createRef(); return ( - <label key={index} ref={ref} css={css`width: 1rem;`} onClick={handler.bind({state_dict: props.state_dict, ref})}> + <label css={[selectorStyles, css`width: 1rem`]} key={index}> <span css={optionStyles}>{option}</span> - <div className="range_dot"/> + <input type="radio" name={props.question_id} css={hiddenInput} onChange={props.handler}/> + <div css={multiSelectInput}/> </label> ); }); return ( - <div css={[containerStyles, rangeDotStyles]}> + <div css={containerStyles}> { range } <div css={sliderContainerStyles}> diff --git a/src/components/InputTypes/index.tsx b/src/components/InputTypes/index.tsx index b1d8afe..d75fbdc 100644 --- a/src/components/InputTypes/index.tsx +++ b/src/components/InputTypes/index.tsx @@ -54,7 +54,7 @@ export default function create_input({ question, public_state }: QuestionProp, h break; case QuestionType.Range: - result = <Range question_id={question.id} options={options} state_dict={public_state}/>; + result = <Range question_id={question.id} options={options} handler={handler}/>; break; case QuestionType.Code: diff --git a/src/components/Question.tsx b/src/components/Question.tsx index 54074f3..e1472e4 100644 --- a/src/components/Question.tsx +++ b/src/components/Question.tsx @@ -46,12 +46,13 @@ class RenderedQuestion extends React.Component<QuestionProp> { let value: string | boolean; switch (event.target.type) { - case QuestionType.Checkbox: - target = this.props.question.id; + case "checkbox": + target = event.target.name; value = event.target.checked; break; - case QuestionType.Radio: + case "radio": + // This handles radios and ranges, as they are both based on the same fundamental input type target = "value"; if (event.target.parentElement) { value = event.target.parentElement.innerText.trimEnd(); @@ -60,10 +61,6 @@ class RenderedQuestion extends React.Component<QuestionProp> { } break; - case QuestionType.Select: - // Handled by component - return; - default: target = "value"; value = event.target.value; |