Skip to content

Commit

Permalink
Add accordion component and fix tab stops in git history
Browse files Browse the repository at this point in the history
  • Loading branch information
riknoll committed Apr 18, 2024
1 parent 2f69369 commit 706dec2
Show file tree
Hide file tree
Showing 8 changed files with 467 additions and 127 deletions.
155 changes: 155 additions & 0 deletions react-common/components/controls/Accordion/Accordion.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import * as React from "react";
import { ContainerProps, classList, fireClickOnEnter } from "../../util";
import { useId } from "../../../hooks/useId";
import { AccordionProvider, clearExpanded, setExpanded, useAccordionDispatch, useAccordionState } from "./context";

export interface AccordionProps extends ContainerProps {
children?: React.ReactElement<AccordionItemProps>[] | React.ReactElement<AccordionItemProps>;
}

export interface AccordionItemProps extends ContainerProps {
children?: [React.ReactElement<AccordionHeaderProps>, React.ReactElement<AccordionPanelProps>];
noChevron?: boolean;
}

export interface AccordionHeaderProps extends ContainerProps {
}

export interface AccordionPanelProps extends ContainerProps {
}

export const Accordion = (props: AccordionProps) => {
const {
children,
id,
className,
ariaLabel,
ariaHidden,
ariaDescribedBy,
role,
} = props;


return (
<AccordionProvider>
<div
className={classList("common-accordion", className)}
id={id}
aria-label={ariaLabel}
aria-hidden={ariaHidden}
aria-describedby={ariaDescribedBy}
role={role}
>
{children}
</div>
</AccordionProvider>
);
};

export const AccordionItem = (props: AccordionItemProps) => {
const {
children,
id,
className,
ariaLabel,
ariaHidden,
ariaDescribedBy,
role,
noChevron
} = props;

const { expanded } = useAccordionState();
const dispatch = useAccordionDispatch();

const panelId = useId();
const mappedChildren = React.Children.toArray(children);
const isExpanded = expanded === panelId;

const onHeaderClick = React.useCallback(() => {
if (isExpanded) {
dispatch(clearExpanded());
}
else {
dispatch(setExpanded(panelId));
}
}, [isExpanded]);


return (
<div
className={classList("common-accordion", className)}
id={id}
aria-label={ariaLabel}
aria-hidden={ariaHidden}
aria-describedby={ariaDescribedBy}
role={role}
>
<button
className="common-accordion-header-outer"
aria-expanded={isExpanded}
aria-controls={panelId}
onClick={onHeaderClick}
onKeyDown={fireClickOnEnter}
>
<div>
{!noChevron &&
<div className="common-accordion-chevron">
<i
className={classList("fas", isExpanded ? "fa-chevron-down" : "fa-chevron-right")}
aria-hidden={true}
/>
</div>
}
<div className="common-accordion-header-content">
{mappedChildren[0]}
</div>
</div>
</button>
<div
id={panelId}
className="common-accordion-panel-outer"
style={{ display: expanded ? "block" : "none" }}
>
{isExpanded && mappedChildren[1]}
</div>
</div>
);
}

export const AccordionHeader = (props: AccordionHeaderProps) => {
const {
id,
className,
ariaLabel,
children,
} = props;

return (
<div
id={id}
className={classList("common-accordion-header", className)}
aria-label={ariaLabel}
>
{children}
</div>
);
}

export const AccordionPanel = (props: AccordionPanelProps) => {
const {
id,
className,
ariaLabel,
children,
} = props;

return (
<div
id={id}
className={classList("common-accordion-body", className)}
aria-label={ariaLabel}
>
{children}
</div>
);
}
70 changes: 70 additions & 0 deletions react-common/components/controls/Accordion/context.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
import * as React from "react";

interface AccordionState {
expanded?: string;
}

const AccordionStateContext = React.createContext<AccordionState>(null);
const AccordionDispatchContext = React.createContext<(action: Action) => void>(null);

export const AccordionProvider = ({ children }: React.PropsWithChildren<{}>) => {
const [state, dispatch] = React.useReducer(
accordionReducer,
{}
);

return (
<AccordionStateContext.Provider value={state}>
<AccordionDispatchContext.Provider value={dispatch}>
{children}
</AccordionDispatchContext.Provider>
</AccordionStateContext.Provider>
)
}

type SetExpanded = {
type: "SET_EXPANDED";
id: string;
};

type ClearExpanded = {
type: "CLEAR_EXPANDED";
};

type Action = SetExpanded | ClearExpanded;

export const setExpanded = (id: string): SetExpanded => (
{
type: "SET_EXPANDED",
id
}
);

export const clearExpanded = (): ClearExpanded => (
{
type: "CLEAR_EXPANDED"
}
);

export function useAccordionState() {
return React.useContext(AccordionStateContext)
}

export function useAccordionDispatch() {
return React.useContext(AccordionDispatchContext);
}

function accordionReducer(state: AccordionState, action: Action): AccordionState {
switch (action.type) {
case "SET_EXPANDED":
return {
...state,
expanded: action.id
};
case "CLEAR_EXPANDED":
return {
...state,
expanded: undefined
};
}
}
1 change: 1 addition & 0 deletions react-common/components/controls/Accordion/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./Accordion";
5 changes: 5 additions & 0 deletions react-common/hooks/useId.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import * as React from "react";

export function useId(): string {
return React.useMemo(() => pxt.Util.guidGen(), []);
}
32 changes: 32 additions & 0 deletions react-common/styles/controls/Accordion.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
.common-accordion-header-outer {
cursor: pointer;
background: none;
color: inherit;
border: none;
padding: 0;
font: inherit;
outline: inherit;
text-align: inherit;
width: 100%;

& > div {
display: flex;
flex-direction: row;
width: 100%;
}

.common-accordion-chevron {
display: flex;
flex-direction: row;
align-items: center;
width: 2rem;
}

.common-accordion-header-content {
flex-grow: 1;
}
}

.common-accordion-header-outer:focus-visible {
outline: @buttonFocusOutlineLightBackground;
}
1 change: 1 addition & 0 deletions react-common/styles/react-common.less
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
@import "controls/Tree.less";
@import "controls/VerticalResizeContainer.less";
@import "controls/VerticalSlider.less";
@import "controls/Accordion.less";
@import "./react-common-variables.less";

@import "fontawesome-free/less/solid.less";
Expand Down
58 changes: 58 additions & 0 deletions theme/github.less
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,64 @@
}
}

.history-zone {
.commit-day {
padding-top: 1rem;
padding-bottom: 1rem;
}

.commit-day:first-child {
padding-top: 0;
}

.commit-day:last-child {
padding-bottom: 0;
}

.commit-day-header {
display: flex;
flex-direction: row;
align-items: center;
}

.commit-day-label {
font-size: 1.28571429em;
font-weight: 700;
margin-right: 0.5rem;
margin-top: 0.2rem;
}

.commit-time {
margin: 0.5rem 0;
font-size: 1rem;
line-height: 1rem;
color: rgba(0, 0, 0, .6);
}

.commit-view {
border-top: 1px solid rgba(34, 36, 38, .15);
padding-top: 1rem;
padding-bottom: 1rem;
position: relative;

.restore-button {
position: absolute;
right: 0;
top: 1.5rem;
z-index: 1;
}
}


.commit-view:first-child {
border-top: none;
}

.commit-view:last-child {
padding-bottom: 0;
}
}

// inverted style
.inverted-theme {
#githubEditor {
Expand Down
Loading

0 comments on commit 706dec2

Please sign in to comment.