summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOskar Nyberg <oskar@mullvad.net>2022-11-03 11:46:07 +0100
committerOskar Nyberg <oskar@mullvad.net>2022-11-04 17:30:51 +0100
commit590638128ad97135f629032422f4ff6688aeaaab (patch)
tree2f124394d2372d3224219c5ef0338c7bc0e945e2
parent2968800c8da2877d7270bef512e543c4dee84223 (diff)
downloadmullvadvpn-590638128ad97135f629032422f4ff6688aeaaab.tar.xz
mullvadvpn-590638128ad97135f629032422f4ff6688aeaaab.zip
Create expandable section component
-rw-r--r--gui/src/renderer/components/ChevronButton.tsx6
-rw-r--r--gui/src/renderer/components/SplitTunnelingSettings.tsx20
-rw-r--r--gui/src/renderer/components/cell/Section.tsx49
-rw-r--r--gui/src/renderer/components/cell/Selector.tsx53
4 files changed, 87 insertions, 41 deletions
diff --git a/gui/src/renderer/components/ChevronButton.tsx b/gui/src/renderer/components/ChevronButton.tsx
index 0485c22caf..6e16fdf6db 100644
--- a/gui/src/renderer/components/ChevronButton.tsx
+++ b/gui/src/renderer/components/ChevronButton.tsx
@@ -2,7 +2,7 @@ import * as React from 'react';
import styled from 'styled-components';
import { colors } from '../../config.json';
-import * as Cell from './cell';
+import { Icon } from './cell/Label';
interface IProps extends React.HTMLAttributes<HTMLButtonElement> {
up: boolean;
@@ -13,7 +13,7 @@ const Button = styled.button({
background: 'none',
});
-const Icon = styled(Cell.Icon)({
+const StyledIcon = styled(Icon)({
flex: 0,
alignSelf: 'stretch',
justifyContent: 'center',
@@ -24,7 +24,7 @@ export default function ChevronButton(props: IProps) {
return (
<Button {...otherProps}>
- <Icon
+ <StyledIcon
tintColor={colors.white80}
tintHoverColor={colors.white}
source={up ? 'icon-chevron-up' : 'icon-chevron-down'}
diff --git a/gui/src/renderer/components/SplitTunnelingSettings.tsx b/gui/src/renderer/components/SplitTunnelingSettings.tsx
index 4cad24f445..f68be19589 100644
--- a/gui/src/renderer/components/SplitTunnelingSettings.tsx
+++ b/gui/src/renderer/components/SplitTunnelingSettings.tsx
@@ -410,6 +410,16 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
splitTunnelingEnabled &&
(!filteredNonSplitApplications || filteredNonSplitApplications.length > 0);
+ const excludedTitle = (
+ <Cell.SectionTitle>
+ {messages.pgettext('split-tunneling-view', 'Excluded apps')}
+ </Cell.SectionTitle>
+ );
+
+ const allTitle = (
+ <Cell.SectionTitle>{messages.pgettext('split-tunneling-view', 'All apps')}</Cell.SectionTitle>
+ );
+
return (
<>
<SettingsHeader>
@@ -428,10 +438,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
{splitTunnelingEnabled && <SearchBar searchTerm={searchTerm} onSearch={setSearchTerm} />}
<Accordion expanded={showSplitSection}>
- <Cell.Section>
- <Cell.SectionTitle>
- {messages.pgettext('split-tunneling-view', 'Excluded apps')}
- </Cell.SectionTitle>
+ <Cell.Section sectionTitle={excludedTitle}>
<ApplicationList
applications={filteredSplitApplications}
rowRenderer={excludedRowRenderer}
@@ -440,10 +447,7 @@ export function WindowsSplitTunnelingSettings(props: IPlatformSplitTunnelingSett
</Accordion>
<Accordion expanded={showNonSplitSection}>
- <Cell.Section>
- <Cell.SectionTitle>
- {messages.pgettext('split-tunneling-view', 'All apps')}
- </Cell.SectionTitle>
+ <Cell.Section sectionTitle={allTitle}>
<ApplicationList
applications={filteredNonSplitApplications}
rowRenderer={includedRowRenderer}
diff --git a/gui/src/renderer/components/cell/Section.tsx b/gui/src/renderer/components/cell/Section.tsx
index 38df17ebdb..1971e8dcb7 100644
--- a/gui/src/renderer/components/cell/Section.tsx
+++ b/gui/src/renderer/components/cell/Section.tsx
@@ -2,7 +2,11 @@ import React from 'react';
import styled from 'styled-components';
import { colors } from '../../../config.json';
+import { useBoolean } from '../../lib/utilityHooks';
+import Accordion from '../Accordion';
+import ChevronButton from '../ChevronButton';
import { buttonText, openSans, sourceSansPro } from '../common-styles';
+import { Container } from './Container';
import { Row } from './Row';
const StyledSection = styled.div({
@@ -25,11 +29,50 @@ export const SectionTitle = styled(Row)(buttonText, (props: SectionTitleProps) =
export const CellSectionContext = React.createContext<boolean>(false);
-export function Section(props: React.HTMLAttributes<HTMLDivElement>) {
- const { children, ...otherProps } = props;
+interface SectionProps extends React.HTMLAttributes<HTMLDivElement> {
+ sectionTitle?: React.ReactElement;
+}
+
+export function Section(props: SectionProps) {
+ const { children, sectionTitle, ...otherProps } = props;
return (
<StyledSection {...otherProps}>
- <CellSectionContext.Provider value={true}>{children}</CellSectionContext.Provider>
+ <CellSectionContext.Provider value={true}>
+ {sectionTitle && <StyledTitleContainer>{sectionTitle}</StyledTitleContainer>}
+ {children}
+ </CellSectionContext.Provider>
</StyledSection>
);
}
+
+const StyledChevronButton = styled(ChevronButton)({
+ padding: 0,
+ marginRight: '16px',
+});
+
+const StyledTitleContainer = styled(Container)({
+ display: 'flex',
+ padding: 0,
+});
+
+interface ExpandableSectionProps extends SectionProps {
+ expandedInitially?: boolean;
+}
+
+export function ExpandableSection(props: ExpandableSectionProps) {
+ const { expandedInitially, sectionTitle, ...otherProps } = props;
+ const [expanded, , , toggleExpanded] = useBoolean(!!expandedInitially);
+
+ const title = (
+ <>
+ {sectionTitle}
+ <StyledChevronButton up={expanded} onClick={toggleExpanded} />
+ </>
+ );
+
+ return (
+ <Section className={props.className} sectionTitle={title} {...otherProps}>
+ <Accordion expanded={expanded}>{props.children}</Accordion>
+ </Section>
+ );
+}
diff --git a/gui/src/renderer/components/cell/Selector.tsx b/gui/src/renderer/components/cell/Selector.tsx
index cb0d6344b6..cfd2357d6c 100644
--- a/gui/src/renderer/components/cell/Selector.tsx
+++ b/gui/src/renderer/components/cell/Selector.tsx
@@ -3,28 +3,15 @@ import styled from 'styled-components';
import { colors } from '../../../config.json';
import { messages } from '../../../shared/gettext';
-import { useBoolean } from '../../lib/utilityHooks';
-import Accordion from '../Accordion';
import { AriaDetails, AriaInput, AriaLabel } from '../AriaGroup';
-import ChevronButton from '../ChevronButton';
import { normalText } from '../common-styles';
import InfoButton from '../InfoButton';
import * as Cell from '.';
-const StyledTitle = styled(Cell.Container)({
- display: 'flex',
- padding: 0,
-});
-
const StyledTitleLabel = styled(Cell.SectionTitle)({
flex: 1,
});
-const StyledChevronButton = styled(ChevronButton)({
- padding: 0,
- marginRight: '16px',
-});
-
export interface SelectorItem<T> {
label: string;
value: T;
@@ -52,8 +39,6 @@ interface SelectorProps<T, U> extends CommonSelectorProps<T, U> {
}
export default function Selector<T, U>(props: SelectorProps<T, U>) {
- const [expanded, , , toggleExpanded] = useBoolean(!props.expandable);
-
const items = props.items.map((item) => {
const selected = props.value === item.value;
const ref = selected ? (props.selectedCellRef as React.Ref<HTMLButtonElement>) : undefined;
@@ -88,8 +73,8 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) {
);
}
- const title = props.title && (
- <StyledTitle>
+ const title = props.title ? (
+ <>
<AriaLabel>
<StyledTitleLabel as="label" disabled={props.disabled} thin={props.thinTitle}>
{props.title}
@@ -100,8 +85,9 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) {
<InfoButton>{props.details}</InfoButton>
</AriaDetails>
)}
- {props.expandable && <StyledChevronButton up={expanded} onClick={toggleExpanded} />}
- </StyledTitle>
+ </>
+ ) : (
+ <></>
);
// Add potential additional items to the list. Used for custom entry.
@@ -112,14 +98,27 @@ export default function Selector<T, U>(props: SelectorProps<T, U>) {
</Cell.Group>
);
- return (
- <AriaInput>
- <Cell.Section role="listbox" className={props.className}>
- {title}
- {props.expandable ? <Accordion expanded={expanded}>{children}</Accordion> : children}
- </Cell.Section>
- </AriaInput>
- );
+ if (props.expandable) {
+ return (
+ <AriaInput>
+ <Cell.ExpandableSection
+ role="listbox"
+ expandedInitially={false}
+ className={props.className}
+ sectionTitle={title}>
+ {children}
+ </Cell.ExpandableSection>
+ </AriaInput>
+ );
+ } else {
+ return (
+ <AriaInput>
+ <Cell.Section role="listbox" className={props.className} sectionTitle={title}>
+ {children}
+ </Cell.Section>
+ </AriaInput>
+ );
+ }
}
const StyledCellIcon = styled(Cell.Icon)((props: { visible: boolean }) => ({