split SettingsSection out of SettingsTab, replace usage (#10735)
* split SettingsSection out of SettingsTab, replace usage * correct copyright * fix VoipRoomSettingsTab
This commit is contained in:
parent
35376996b0
commit
58c942be45
9 changed files with 174 additions and 72 deletions
|
@ -339,6 +339,7 @@
|
||||||
@import "./views/settings/_SpellCheckLanguages.pcss";
|
@import "./views/settings/_SpellCheckLanguages.pcss";
|
||||||
@import "./views/settings/_ThemeChoicePanel.pcss";
|
@import "./views/settings/_ThemeChoicePanel.pcss";
|
||||||
@import "./views/settings/_UpdateCheckButton.pcss";
|
@import "./views/settings/_UpdateCheckButton.pcss";
|
||||||
|
@import "./views/settings/tabs/_SettingsSection.pcss";
|
||||||
@import "./views/settings/tabs/_SettingsTab.pcss";
|
@import "./views/settings/tabs/_SettingsTab.pcss";
|
||||||
@import "./views/settings/tabs/room/_GeneralRoomSettingsTab.pcss";
|
@import "./views/settings/tabs/room/_GeneralRoomSettingsTab.pcss";
|
||||||
@import "./views/settings/tabs/room/_NotificationSettingsTab.pcss";
|
@import "./views/settings/tabs/room/_NotificationSettingsTab.pcss";
|
||||||
|
|
|
@ -20,9 +20,11 @@ limitations under the License.
|
||||||
}
|
}
|
||||||
|
|
||||||
.mx_SettingsFieldset_legend {
|
.mx_SettingsFieldset_legend {
|
||||||
font-size: $font-16px;
|
// matches h3
|
||||||
display: block;
|
font-size: $font-18px;
|
||||||
font-weight: var(--font-semi-bold);
|
font-weight: var(--font-semi-bold);
|
||||||
|
line-height: $font-22px;
|
||||||
|
display: block;
|
||||||
color: $primary-content;
|
color: $primary-content;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
|
|
36
res/css/views/settings/tabs/_SettingsSection.pcss
Normal file
36
res/css/views/settings/tabs/_SettingsSection.pcss
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.mx_SettingsSection {
|
||||||
|
--SettingsTab_section-margin-bottom-preferences-labs: 30px;
|
||||||
|
--SettingsTab_heading_nth_child-margin-top: 30px; /* TODO: Use a spacing variable */
|
||||||
|
--SettingsTab_fullWidthField-margin-inline-end: 100px;
|
||||||
|
--SettingsTab_tooltip-max-width: 120px; /* So it fits in the space provided by the page */
|
||||||
|
|
||||||
|
color: $primary-content;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: $links;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mx_SettingsSection_subSections {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
grid-gap: $spacing-32;
|
||||||
|
|
||||||
|
padding: $spacing-16 0;
|
||||||
|
}
|
48
src/components/views/settings/shared/SettingsSection.tsx
Normal file
48
src/components/views/settings/shared/SettingsSection.tsx
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React, { HTMLAttributes } from "react";
|
||||||
|
|
||||||
|
import Heading from "../../typography/Heading";
|
||||||
|
|
||||||
|
export interface SettingsSectionProps extends HTMLAttributes<HTMLDivElement> {
|
||||||
|
heading: string | React.ReactNode;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A section of settings content
|
||||||
|
* A SettingsTab may contain one or more SettingsSections
|
||||||
|
* Eg:
|
||||||
|
* ```
|
||||||
|
* <SettingsTab>
|
||||||
|
* <SettingsSection heading="General">
|
||||||
|
* <SettingsSubsection heading="Profile">
|
||||||
|
* // profile settings form
|
||||||
|
* <SettingsSubsection>
|
||||||
|
* <SettingsSubsection heading="Account">
|
||||||
|
* // account settings
|
||||||
|
* <SettingsSubsection>
|
||||||
|
* </SettingsSection>
|
||||||
|
* </SettingsTab>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
export const SettingsSection: React.FC<SettingsSectionProps> = ({ heading, children, ...rest }) => (
|
||||||
|
<div {...rest} className="mx_SettingsSection">
|
||||||
|
{typeof heading === "string" ? <Heading size="h2">{heading}</Heading> : <>{heading}</>}
|
||||||
|
<div className="mx_SettingsSection_subSections">{children}</div>
|
||||||
|
</div>
|
||||||
|
);
|
|
@ -15,16 +15,30 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
import React from "react";
|
import React from "react";
|
||||||
|
|
||||||
import Heading from "../../typography/Heading";
|
|
||||||
|
|
||||||
export interface SettingsTabProps {
|
export interface SettingsTabProps {
|
||||||
heading: string;
|
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SettingsTab: React.FC<SettingsTabProps> = ({ heading, children }) => (
|
/**
|
||||||
|
* Container for a tab of settings panel content
|
||||||
|
* Should contain one or more SettingsSection
|
||||||
|
* Settings width, padding and spacing between sections
|
||||||
|
* Eg:
|
||||||
|
* ```
|
||||||
|
* <SettingsTab>
|
||||||
|
* <SettingsSection heading="General">
|
||||||
|
* <SettingsSubsection heading="Profile">
|
||||||
|
* // profile settings form
|
||||||
|
* <SettingsSubsection>
|
||||||
|
* <SettingsSubsection heading="Account">
|
||||||
|
* // account settings
|
||||||
|
* <SettingsSubsection>
|
||||||
|
* </SettingsSection>
|
||||||
|
* </SettingsTab>
|
||||||
|
* ```
|
||||||
|
*/
|
||||||
|
const SettingsTab: React.FC<SettingsTabProps> = ({ children }) => (
|
||||||
<div className="mx_SettingsTab">
|
<div className="mx_SettingsTab">
|
||||||
<Heading size="h2">{heading}</Heading>
|
|
||||||
<div className="mx_SettingsTab_sections">{children}</div>
|
<div className="mx_SettingsTab_sections">{children}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -28,6 +28,7 @@ import SettingsTab from "../SettingsTab";
|
||||||
import { ElementCall } from "../../../../../models/Call";
|
import { ElementCall } from "../../../../../models/Call";
|
||||||
import { useRoomState } from "../../../../../hooks/useRoomState";
|
import { useRoomState } from "../../../../../hooks/useRoomState";
|
||||||
import SdkConfig, { DEFAULTS } from "../../../../../SdkConfig";
|
import SdkConfig, { DEFAULTS } from "../../../../../SdkConfig";
|
||||||
|
import { SettingsSection } from "../../shared/SettingsSection";
|
||||||
|
|
||||||
interface ElementCallSwitchProps {
|
interface ElementCallSwitchProps {
|
||||||
room: Room;
|
room: Room;
|
||||||
|
@ -100,10 +101,12 @@ interface Props {
|
||||||
|
|
||||||
export const VoipRoomSettingsTab: React.FC<Props> = ({ room }) => {
|
export const VoipRoomSettingsTab: React.FC<Props> = ({ room }) => {
|
||||||
return (
|
return (
|
||||||
<SettingsTab heading={_t("Voice & Video")}>
|
<SettingsTab>
|
||||||
<SettingsSubsection heading={_t("Call type")}>
|
<SettingsSection heading={_t("Voice & Video")}>
|
||||||
<ElementCallSwitch room={room} />
|
<SettingsSubsection heading={_t("Call type")}>
|
||||||
</SettingsSubsection>
|
<ElementCallSwitch room={room} />
|
||||||
|
</SettingsSubsection>
|
||||||
|
</SettingsSection>
|
||||||
</SettingsTab>
|
</SettingsTab>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -38,6 +38,7 @@ import { useAsyncMemo } from "../../../../../hooks/useAsyncMemo";
|
||||||
import QuestionDialog from "../../../dialogs/QuestionDialog";
|
import QuestionDialog from "../../../dialogs/QuestionDialog";
|
||||||
import { FilterVariation } from "../../devices/filter";
|
import { FilterVariation } from "../../devices/filter";
|
||||||
import { OtherSessionsSectionHeading } from "../../devices/OtherSessionsSectionHeading";
|
import { OtherSessionsSectionHeading } from "../../devices/OtherSessionsSectionHeading";
|
||||||
|
import { SettingsSection } from "../../shared/SettingsSection";
|
||||||
|
|
||||||
const confirmSignOut = async (sessionsToSignOutCount: number): Promise<boolean> => {
|
const confirmSignOut = async (sessionsToSignOutCount: number): Promise<boolean> => {
|
||||||
const { finished } = Modal.createDialog(QuestionDialog, {
|
const { finished } = Modal.createDialog(QuestionDialog, {
|
||||||
|
@ -225,62 +226,64 @@ const SessionManagerTab: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsTab heading={_t("Sessions")}>
|
<SettingsTab>
|
||||||
<SecurityRecommendations
|
<SettingsSection heading={_t("Sessions")}>
|
||||||
devices={devices}
|
<SecurityRecommendations
|
||||||
goToFilteredList={onGoToFilteredList}
|
devices={devices}
|
||||||
currentDeviceId={currentDeviceId}
|
goToFilteredList={onGoToFilteredList}
|
||||||
/>
|
currentDeviceId={currentDeviceId}
|
||||||
<CurrentDeviceSection
|
/>
|
||||||
device={currentDevice}
|
<CurrentDeviceSection
|
||||||
localNotificationSettings={localNotificationSettings.get(currentDeviceId)}
|
device={currentDevice}
|
||||||
setPushNotifications={setPushNotifications}
|
localNotificationSettings={localNotificationSettings.get(currentDeviceId)}
|
||||||
isSigningOut={signingOutDeviceIds.includes(currentDeviceId)}
|
setPushNotifications={setPushNotifications}
|
||||||
isLoading={isLoadingDeviceList}
|
isSigningOut={signingOutDeviceIds.includes(currentDeviceId)}
|
||||||
saveDeviceName={(deviceName) => saveDeviceName(currentDeviceId, deviceName)}
|
isLoading={isLoadingDeviceList}
|
||||||
onVerifyCurrentDevice={onVerifyCurrentDevice}
|
saveDeviceName={(deviceName) => saveDeviceName(currentDeviceId, deviceName)}
|
||||||
onSignOutCurrentDevice={onSignOutCurrentDevice}
|
onVerifyCurrentDevice={onVerifyCurrentDevice}
|
||||||
signOutAllOtherSessions={signOutAllOtherSessions}
|
onSignOutCurrentDevice={onSignOutCurrentDevice}
|
||||||
otherSessionsCount={otherSessionsCount}
|
signOutAllOtherSessions={signOutAllOtherSessions}
|
||||||
/>
|
otherSessionsCount={otherSessionsCount}
|
||||||
{shouldShowOtherSessions && (
|
/>
|
||||||
<SettingsSubsection
|
{shouldShowOtherSessions && (
|
||||||
heading={
|
<SettingsSubsection
|
||||||
<OtherSessionsSectionHeading
|
heading={
|
||||||
otherSessionsCount={otherSessionsCount}
|
<OtherSessionsSectionHeading
|
||||||
signOutAllOtherSessions={signOutAllOtherSessions!}
|
otherSessionsCount={otherSessionsCount}
|
||||||
disabled={!!signingOutDeviceIds.length}
|
signOutAllOtherSessions={signOutAllOtherSessions!}
|
||||||
/>
|
disabled={!!signingOutDeviceIds.length}
|
||||||
}
|
/>
|
||||||
description={_t(
|
|
||||||
`For best security, verify your sessions and sign out ` +
|
|
||||||
`from any session that you don't recognize or use anymore.`,
|
|
||||||
)}
|
|
||||||
data-testid="other-sessions-section"
|
|
||||||
>
|
|
||||||
<FilteredDeviceList
|
|
||||||
devices={otherDevices}
|
|
||||||
pushers={pushers}
|
|
||||||
localNotificationSettings={localNotificationSettings}
|
|
||||||
filter={filter}
|
|
||||||
expandedDeviceIds={expandedDeviceIds}
|
|
||||||
signingOutDeviceIds={signingOutDeviceIds}
|
|
||||||
selectedDeviceIds={selectedDeviceIds}
|
|
||||||
setSelectedDeviceIds={setSelectedDeviceIds}
|
|
||||||
onFilterChange={setFilter}
|
|
||||||
onDeviceExpandToggle={onDeviceExpandToggle}
|
|
||||||
onRequestDeviceVerification={
|
|
||||||
requestDeviceVerification ? onTriggerDeviceVerification : undefined
|
|
||||||
}
|
}
|
||||||
onSignOutDevices={onSignOutOtherDevices}
|
description={_t(
|
||||||
saveDeviceName={saveDeviceName}
|
`For best security, verify your sessions and sign out ` +
|
||||||
setPushNotifications={setPushNotifications}
|
`from any session that you don't recognize or use anymore.`,
|
||||||
ref={filteredDeviceListRef}
|
)}
|
||||||
supportsMSC3881={supportsMSC3881}
|
data-testid="other-sessions-section"
|
||||||
/>
|
>
|
||||||
</SettingsSubsection>
|
<FilteredDeviceList
|
||||||
)}
|
devices={otherDevices}
|
||||||
<LoginWithQRSection onShowQr={onShowQrClicked} versions={clientVersions} capabilities={capabilities} />
|
pushers={pushers}
|
||||||
|
localNotificationSettings={localNotificationSettings}
|
||||||
|
filter={filter}
|
||||||
|
expandedDeviceIds={expandedDeviceIds}
|
||||||
|
signingOutDeviceIds={signingOutDeviceIds}
|
||||||
|
selectedDeviceIds={selectedDeviceIds}
|
||||||
|
setSelectedDeviceIds={setSelectedDeviceIds}
|
||||||
|
onFilterChange={setFilter}
|
||||||
|
onDeviceExpandToggle={onDeviceExpandToggle}
|
||||||
|
onRequestDeviceVerification={
|
||||||
|
requestDeviceVerification ? onTriggerDeviceVerification : undefined
|
||||||
|
}
|
||||||
|
onSignOutDevices={onSignOutOtherDevices}
|
||||||
|
saveDeviceName={saveDeviceName}
|
||||||
|
setPushNotifications={setPushNotifications}
|
||||||
|
ref={filteredDeviceListRef}
|
||||||
|
supportsMSC3881={supportsMSC3881}
|
||||||
|
/>
|
||||||
|
</SettingsSubsection>
|
||||||
|
)}
|
||||||
|
<LoginWithQRSection onShowQr={onShowQrClicked} versions={clientVersions} capabilities={capabilities} />
|
||||||
|
</SettingsSection>
|
||||||
</SettingsTab>
|
</SettingsTab>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -22,7 +22,7 @@ import SettingsTab, { SettingsTabProps } from "../../../../../src/components/vie
|
||||||
describe("<SettingsTab />", () => {
|
describe("<SettingsTab />", () => {
|
||||||
const getComponent = (props: SettingsTabProps): ReactElement => <SettingsTab {...props} />;
|
const getComponent = (props: SettingsTabProps): ReactElement => <SettingsTab {...props} />;
|
||||||
it("renders tab", () => {
|
it("renders tab", () => {
|
||||||
const { container } = render(getComponent({ heading: "Test Tab", children: <div>test</div> }));
|
const { container } = render(getComponent({ children: <div>test</div> }));
|
||||||
|
|
||||||
expect(container).toMatchSnapshot();
|
expect(container).toMatchSnapshot();
|
||||||
});
|
});
|
||||||
|
|
|
@ -5,11 +5,6 @@ exports[`<SettingsTab /> renders tab 1`] = `
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsTab"
|
class="mx_SettingsTab"
|
||||||
>
|
>
|
||||||
<h2
|
|
||||||
class="mx_Heading_h2"
|
|
||||||
>
|
|
||||||
Test Tab
|
|
||||||
</h2>
|
|
||||||
<div
|
<div
|
||||||
class="mx_SettingsTab_sections"
|
class="mx_SettingsTab_sections"
|
||||||
>
|
>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue