Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -237,7 +237,8 @@
"xml2js": "0.5.0",
"@stablelib/utf8": "1.0.2",
"dexie-encrypted@2.0.0": "patch:dexie-encrypted@npm%3A2.0.0#./.yarn/patches/dexie-encrypted-npm-2.0.0-eb61eb5975.patch",
"axios": "^1.9.0"
"axios": "^1.9.0",
"js-yaml": "^4.1.0"
},
"version": "0.27.0",
"packageManager": "yarn@4.1.1"
Expand Down
4 changes: 4 additions & 0 deletions src/i18n/en-US.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@
"authLoginTitle": "Log in",
"authPlaceholderEmail": "Email",
"authPlaceholderPassword": "Password",
"showTogglePasswordLabel": "Show password",
"hideTogglePasswordLabel": "Hide password",
"authPostedResend": "Resend to {email}",
"authPostedResendAction": "No email showing up?",
"authPostedResendDetail": "Check your email inbox and follow the instructions.",
Expand Down Expand Up @@ -1969,6 +1971,8 @@
"verify.headline": "You’ve got mail",
"verify.resendCode": "Resend code",
"verify.subhead": "Enter the six-digit verification code we sent to{newline}{email}",
"verify.codeLabel": "Six-digit code",
"verify.codePlaceholder": "Input field, enter digit",
"videoCallMenuMoreAddReaction": "Add reaction",
"videoCallMenuMoreAudioSettings": "Audio Settings",
"videoCallMenuMoreChangeView": "Change view",
Expand Down
4 changes: 4 additions & 0 deletions src/script/auth/component/AccountForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,8 @@ const AccountFormComponent = ({
placeholder={t('accountForm.passwordPlaceholder')}
pattern={ValidationUtil.getNewPasswordPattern(Config.getConfig().NEW_PASSWORD_MINIMUM_LENGTH)}
data-uie-name="enter-password"
showTogglePasswordLabel={t('showTogglePasswordLabel')}
hideTogglePasswordLabel={t('hideTogglePasswordLabel')}
/>
<Text muted css={styles.passwordInfo(!!validationErrors.length)} data-uie-name="element-password-help">
{t('accountForm.passwordHelp', {minPasswordLength: String(Config.getConfig().NEW_PASSWORD_MINIMUM_LENGTH)})}
Expand All @@ -269,6 +271,8 @@ const AccountFormComponent = ({
placeholder={t('accountForm.confirmPasswordPlaceholder')}
pattern={`^${registrationData.password?.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}$`}
data-uie-name="enter-confirm-password"
showTogglePasswordLabel={t('showTogglePasswordLabel')}
hideTogglePasswordLabel={t('hideTogglePasswordLabel')}
/>

<Exception errors={[authError, ...validationErrors]} />
Expand Down
12 changes: 7 additions & 5 deletions src/script/auth/component/BackButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,14 @@
const navigate = useNavigate();

return (
<ArrowIcon
<button
type="button"
onClick={() => navigate(-1)}
direction="left"
data-uie-name="go-index"
aria-label={t('createPersonalAccount.goBack')}
color={COLOR.TEXT}
/>
data-uie-name="go-index"
css={{background: 'none', border: 'none', cursor: 'pointer'}}

Check warning on line 35 in src/script/auth/component/BackButton.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unknown property 'css' found

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZo1QOtBdSRJjQCQo0rq&open=AZo1QOtBdSRJjQCQo0rq&pullRequest=19714
>
<ArrowIcon direction="left" aria-hidden="true" focusable="false" color={COLOR.TEXT} />
</button>
);
};
2 changes: 2 additions & 0 deletions src/script/auth/component/ClientItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,8 @@ const ClientItem = ({selected, onClientRemoval, onClick, client, clientError, re
placeholder={t('clientItem.passwordPlaceholder')}
required
type="password"
showTogglePasswordLabel={t('showTogglePasswordLabel')}
hideTogglePasswordLabel={t('hideTogglePasswordLabel')}
value={password}
/>
</FlexBox>
Expand Down
2 changes: 2 additions & 0 deletions src/script/auth/component/JoinGuestLinkPasswordModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ const JoinGuestLinkPasswordModal = ({
id="guest_link_join_password"
className="modal__input"
type="password"
showTogglePasswordLabel={t('showTogglePasswordLabel')}
hideTogglePasswordLabel={t('hideTogglePasswordLabel')}
autoComplete="off"
value={passwordValue}
onChange={event => setPasswordValue(event.currentTarget.value)}
Expand Down
2 changes: 2 additions & 0 deletions src/script/auth/component/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,8 @@ const LoginForm = ({isFetching, onSubmit}: LoginFormProps) => {
pattern={'.{1,1024}'}
required
data-uie-name="enter-password"
showTogglePasswordLabel={t('showTogglePasswordLabel')}
hideTogglePasswordLabel={t('hideTogglePasswordLabel')}
/>

{isFetching ? (
Expand Down
25 changes: 25 additions & 0 deletions src/script/auth/component/RouteA11y.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {useRouteA11y} from '../hooks/useRouteA11y';

export const RouteA11y: React.FC = (): null => {
useRouteA11y();
return null;
};
53 changes: 53 additions & 0 deletions src/script/auth/hooks/useRouteA11y.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Wire
* Copyright (C) 2025 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*
*/

import {useEffect} from 'react';

import {useLocation} from 'react-router-dom';

export function useRouteA11y(screenKey?: string) {
const location = useLocation();

useEffect(() => {
const focusTarget: HTMLElement | null =
document.querySelector<HTMLElement>('[data-page-title]') ||
document.querySelector<HTMLElement>('main,[role="main"]') ||
document.querySelector<HTMLElement>('h1');

if (!focusTarget) {
return;
}

// scroll to top on each route change
window.scrollTo({top: 0, left: 0});

const element = focusTarget;
element.setAttribute('tabindex', '-1');
element.classList.add('sr-only-focus');
element.focus({preventScroll: true});

// remove tabindex after blur
const handleBlur = () => {
element.classList.remove('sr-only-focus');
element.removeAttribute('tabindex');
element.removeEventListener('blur', handleBlur);
};
element.addEventListener('blur', handleBlur);
}, [location.key, screenKey]);
}
4 changes: 3 additions & 1 deletion src/script/auth/page/CreatePersonalAccount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@
<div css={styles.backButtonContainer}>
<BackButton />
</div>
<p css={styles.header}>{t('createPersonalAccount.headLine')}</p>
<p css={styles.header} role="heading" aria-level={1} data-page-title tabIndex={-1}>

Check warning on line 54 in src/script/auth/page/CreatePersonalAccount.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <h1>, <h2>, <h3>, <h4>, <h5>, or <h6> instead of the "heading" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZo1AG4upseksdDtwjCT&open=AZo1AG4upseksdDtwjCT&pullRequest=19714
{t('createPersonalAccount.headLine')}
</p>
<AccountForm onSubmit={onSubmit} />
<p css={styles.footer}>{t('createPersonalAccount.subHeader')}</p>
<a css={styles.teamCreateButton} href={EXTERNAL_ROUTE.WIRE_TEAMS_SIGNUP} target="_blank" rel="noreferrer">
Expand Down
52 changes: 44 additions & 8 deletions src/script/auth/page/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,14 @@
import {ClientType} from '@wireapp/api-client/lib/client/index';
import {BackendError, BackendErrorLabel, SyntheticErrorLabel} from '@wireapp/api-client/lib/http/';
import {StatusCodes} from 'http-status-codes';
import {FormattedMessage} from 'react-intl';
import {connect} from 'react-redux';
import {useNavigate} from 'react-router-dom';
import {AnyAction, Dispatch} from 'redux';

import {Runtime, UrlUtil} from '@wireapp/commons';
import {
ActionLinkButton,
Button,
ButtonVariant,
Checkbox,
Expand All @@ -49,7 +51,6 @@
QUERY,
QueryKeys,
Text,
TextLink,
useMatchMedia,
} from '@wireapp/react-ui-kit';

Expand All @@ -61,6 +62,7 @@
import {EntropyContainer} from './EntropyContainer';
import {separator} from './Login.styles';
import {Page} from './Page';
import {styles} from './VerifyEmailCode.styles';

import {Config} from '../../Config';
import {AccountAlreadyExistsModal} from '../component/AccountAlreadyExistsModal';
Expand All @@ -70,6 +72,7 @@
import {JoinGuestLinkPasswordModal} from '../component/JoinGuestLinkPasswordModal';
import {LoginForm} from '../component/LoginForm';
import {EXTERNAL_ROUTE} from '../externalRoute';
import {useRouteA11y} from '../hooks/useRouteA11y';
import {actionRoot} from '../module/action/';
import {LabeledError} from '../module/action/LabeledError';
import {ValidationError} from '../module/action/ValidationError';
Expand Down Expand Up @@ -129,6 +132,11 @@

const isOauth = UrlUtil.hasURLParameter(QUERY_KEY.SCOPE, window.location.hash);

const [routeAction, setRouteAction] = useState<'login' | 'verify'>('login');

// this triggers the hook every time screen changes under the same route
useRouteA11y(routeAction);

const {
ENABLE_ACCOUNT_REGISTRATION: isAccountRegistrationEnabled,
ENABLE_DOMAIN_DISCOVERY: isDomainDiscoveryEnabled,
Expand Down Expand Up @@ -296,6 +304,7 @@
if (login.email || login.handle) {
await doSendTwoFactorCode(login.email || login.handle || '');
setTwoFactorLoginData(login);
setRouteAction('verify');
await doSetLocalStorage(QUERY_KEY.JOIN_EXPIRES, Date.now() + 1000 * 60 * 10);
}
break;
Expand Down Expand Up @@ -423,18 +432,35 @@
>
{twoFactorLoginData ? (
<div>
<Text fontSize="1.5rem" css={{fontWeight: '500'}} center block>
<Text
fontSize="1.5rem"
css={{fontWeight: '500'}}
center
block
role="heading"
aria-level={1}
data-page-title
tabIndex={-1}
>

Check warning on line 444 in src/script/auth/page/Login.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <h1>, <h2>, <h3>, <h4>, <h5>, or <h6> instead of the "heading" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZp3uHEz8y1demWuRssg&open=AZp3uHEz8y1demWuRssg&pullRequest=19714
{t('login.twoFactorLoginTitle')}
</Text>
<Text data-uie-name="label-with-email" fontSize="1rem">
{t('login.twoFactorLoginSubHead', {email: twoFactorLoginData.email as string})}
<Text block data-uie-name="label-with-email" fontSize="1rem" css={styles.subhead}>
<FormattedMessage
id="login.twoFactorLoginSubHead"
values={{
email: twoFactorLoginData.email as string,
newline: <br />,
}}
/>
</Text>
<Label markInvalid={!!twoFactorSubmitError}>
<CodeInput
disabled={isFetching}
style={{marginTop: '1rem'}}
onCodeComplete={submitTwoFactorLogin}
data-uie-name="enter-code"
codeInputLabel={t('verify.codeLabel')}
codePlaceholder={t('verify.codePlaceholder')}
/>
</Label>
<div style={{display: 'flex', justifyContent: 'center', marginTop: 10}}>
Expand All @@ -444,9 +470,13 @@
{isSendingTwoFactorCode ? (
<Loading size={20} />
) : (
<TextLink onClick={resendTwoFactorCode} center data-uie-name="do-resend-code">
<ActionLinkButton
onClick={resendTwoFactorCode}
data-uie-name="do-resend-code"
css={styles.resendLink}
>
{t('verify.resendCode')}
</TextLink>
</ActionLinkButton>
)}
</div>
<FlexBox justify="center">
Expand All @@ -464,11 +494,17 @@
<>
<div>
{isEnterpriseLoginV2Enabled ? (
<div css={{fontWeight: '500', fontSize: '1.5rem', marginBottom: '2rem'}}>
<div
css={{fontWeight: '500', fontSize: '1.5rem', marginBottom: '2rem'}}

Check warning on line 498 in src/script/auth/page/Login.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unknown property 'css' found

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZo1AG3qpseksdDtwjCS&open=AZo1AG3qpseksdDtwjCS&pullRequest=19714
role="heading"
aria-level={1}
data-page-title
tabIndex={-1}
>

Check warning on line 503 in src/script/auth/page/Login.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <h1>, <h2>, <h3>, <h4>, <h5>, or <h6> instead of the "heading" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZo1AG3qpseksdDtwjCR&open=AZo1AG3qpseksdDtwjCR&pullRequest=19714
{t('index.welcome', {brandName: Config.getConfig().BACKEND_NAME})}
</div>
) : (
<Heading level={embedded ? '2' : '1'} center>
<Heading level={embedded ? '2' : '1'} center data-page-title tabIndex={-1}>
{t('login.headline')}
</Heading>
)}
Expand Down
11 changes: 11 additions & 0 deletions src/script/auth/page/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ import {VerifyEmailCode} from './VerifyEmailCode';
import {VerifyEmailLink} from './VerifyEmailLink';

import {Config} from '../../Config';
import {RouteA11y} from '../component/RouteA11y';
import {mapLanguage, normalizeLanguage} from '../localeConfig';
import {actionRoot as ROOT_ACTIONS} from '../module/action/';
import {bindActionCreators, RootState} from '../module/reducer';
Expand All @@ -73,6 +74,15 @@ const RootComponent: FC<RootProps & ConnectedProps & DispatchProps> = ({
isFetchingSSOSettings,
doGetSSOSettings,
}) => {
// Injects the helper class used by useRouteA11y so programmatic focus targets (for screen readers)
// lose their outlines while the focus trap is active.
useEffect(() => {
const style = document.createElement('style');
style.textContent = `.sr-only-focus:focus, .sr-only-focus:focus-visible { outline: none !important; }`;
document.head.appendChild(style);
return () => style.remove();
}, []);

useEffect(() => {
// Force the hash url to have a initial `/` (see https://stackoverflow.com/a/71864506)
const forceSlashAfterHash = () => {
Expand Down Expand Up @@ -146,6 +156,7 @@ const RootComponent: FC<RootProps & ConnectedProps & DispatchProps> = ({
</ContainerXS>
) : (
<Router>
<RouteA11y />
<Routes>
<Route
path={ROUTE.INDEX}
Expand Down
4 changes: 3 additions & 1 deletion src/script/auth/page/SetAccountType.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@
<FlexBox css={styles.headerIcon}>
<BackButton />
</FlexBox>
<FlexBox css={styles.headerText}>{t('selectAccountTypeHeading')}</FlexBox>
<FlexBox css={styles.headerText} role="heading" aria-level={1} data-page-title tabIndex={-1}>

Check warning on line 84 in src/script/auth/page/SetAccountType.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <h1>, <h2>, <h3>, <h4>, <h5>, or <h6> instead of the "heading" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZo1AGx2pseksdDtwjCO&open=AZo1AGx2pseksdDtwjCO&pullRequest=19714
{t('selectAccountTypeHeading')}
</FlexBox>
</FlexBox>
<FlexBox css={styles.optionWrapper}>
{accountTypeOptions.map((option, index) => (
Expand Down
2 changes: 2 additions & 0 deletions src/script/auth/page/SetPassword.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ const SetPasswordComponent = ({
name="password"
placeholder={t('setPassword.passwordPlaceholder')}
type="password"
showTogglePasswordLabel={t('showTogglePasswordLabel')}
hideTogglePasswordLabel={t('hideTogglePasswordLabel')}
markInvalid={!isValidPassword}
onChange={(event: React.ChangeEvent<HTMLInputElement>) => {
passwordInput.current.setCustomValidity('');
Expand Down
8 changes: 7 additions & 1 deletion src/script/auth/page/SingleSignOn.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,13 @@
<div>
{isEnterpriseLoginV2Enabled ? (
<>
<div css={{fontWeight: '500', fontSize: '1.5rem'}}>
<div
css={{fontWeight: '500', fontSize: '1.5rem'}}

Check warning on line 263 in src/script/auth/page/SingleSignOn.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Unknown property 'css' found

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZo1AG20pseksdDtwjCQ&open=AZo1AG20pseksdDtwjCQ&pullRequest=19714
role="heading"
aria-level={1}
data-page-title
tabIndex={-1}
>

Check warning on line 268 in src/script/auth/page/SingleSignOn.tsx

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use <h1>, <h2>, <h3>, <h4>, <h5>, or <h6> instead of the "heading" role to ensure accessibility across all devices.

See more on https://sonarcloud.io/project/issues?id=wireapp_wire-webapp&issues=AZo1AG20pseksdDtwjCP&open=AZo1AG20pseksdDtwjCP&pullRequest=19714
{t('index.welcome', {brandName: Config.getConfig().BACKEND_NAME})}
</div>

Expand Down
Loading
Loading