mirror of
https://github.com/overleaf/overleaf.git
synced 2024-11-29 03:53:40 -05:00
Merge pull request #19126 from overleaf/td-bs5-contact-modal-react
React version of Contact Us modal GitOrigin-RevId: 0bef3095f36daa88afdc6172a5531ed11e892047
This commit is contained in:
parent
fb114a7c44
commit
34fc43d59a
33 changed files with 484 additions and 210 deletions
|
@ -555,6 +555,7 @@
|
|||
"have_more_days_to_try": "",
|
||||
"headers": "",
|
||||
"help": "",
|
||||
"help_articles_matching": "",
|
||||
"help_improve_overleaf_fill_out_this_survey": "",
|
||||
"help_improve_screen_reader_fill_out_this_survey": "",
|
||||
"hide_configuration": "",
|
||||
|
@ -686,10 +687,12 @@
|
|||
"joined_team": "",
|
||||
"joining": "",
|
||||
"justify": "",
|
||||
"kb_suggestions_enquiry": "",
|
||||
"keep_current_plan": "",
|
||||
"keep_personal_projects_separate": "",
|
||||
"keep_your_account_safe_add_another_email": "",
|
||||
"keybindings": "",
|
||||
"knowledge_base": "",
|
||||
"labels_help_you_to_easily_reference_your_figures": "",
|
||||
"labels_help_you_to_reference_your_tables": "",
|
||||
"language_feedback": "",
|
||||
|
@ -976,6 +979,8 @@
|
|||
"please_enter_confirmation_code": "",
|
||||
"please_get_in_touch": "",
|
||||
"please_link_before_making_primary": "",
|
||||
"please_provide_a_message": "",
|
||||
"please_provide_a_subject": "",
|
||||
"please_reconfirm_institutional_email": "",
|
||||
"please_reconfirm_your_affiliation_before_making_this_primary": "",
|
||||
"please_refresh": "",
|
||||
|
|
|
@ -24,10 +24,12 @@ export function formatWikiHit(hit) {
|
|||
const pagePath = hit.kb ? 'how-to' : 'latex'
|
||||
|
||||
let pageAnchor = ''
|
||||
let pageName = hit._highlightResult.pageName.value
|
||||
if (hit.sectionName) {
|
||||
pageAnchor = `#${hit.sectionName.replace(/\s/g, '_')}`
|
||||
pageName += ' - ' + hit.sectionName
|
||||
const rawPageName = hit._highlightResult.pageName.value
|
||||
const sectionName = hit.sectionName
|
||||
let pageName = rawPageName
|
||||
if (sectionName) {
|
||||
pageAnchor = `#${sectionName.replace(/\s/g, '_')}`
|
||||
pageName += ' - ' + sectionName
|
||||
}
|
||||
|
||||
const body = hit._highlightResult.content.value
|
||||
|
@ -37,5 +39,5 @@ export function formatWikiHit(hit) {
|
|||
.join('\n...\n')
|
||||
|
||||
const url = `/learn/${pagePath}/${pageSlug}${pageAnchor}`
|
||||
return { url, pageName, content }
|
||||
return { url, pageName, rawPageName, sectionName, content }
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ export function setupSearch(formEl) {
|
|||
const iconEl = document.createElement('i')
|
||||
iconEl.className = 'fa fa-angle-right'
|
||||
iconEl.setAttribute('aria-hidden', 'true')
|
||||
linkEl.append(contentEl)
|
||||
linkEl.append(iconEl)
|
||||
|
||||
resultsEl.append(liEl)
|
||||
}
|
||||
|
|
|
@ -118,18 +118,20 @@ function AccountInfoSection() {
|
|||
</OLFormGroup>
|
||||
) : null}
|
||||
{canUpdateEmail || canUpdateNames ? (
|
||||
<OLButton
|
||||
type="submit"
|
||||
variant="primary"
|
||||
form="account-info-form"
|
||||
disabled={!isFormValid}
|
||||
isLoading={isLoading}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('update'),
|
||||
}}
|
||||
>
|
||||
{t('update')}
|
||||
</OLButton>
|
||||
<OLFormGroup>
|
||||
<OLButton
|
||||
type="submit"
|
||||
variant="primary"
|
||||
form="account-info-form"
|
||||
disabled={!isFormValid}
|
||||
isLoading={isLoading}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('update'),
|
||||
}}
|
||||
>
|
||||
{t('update')}
|
||||
</OLButton>
|
||||
</OLFormGroup>
|
||||
) : null}
|
||||
</form>
|
||||
</>
|
||||
|
@ -188,7 +190,9 @@ function ReadOrWriteFormGroup({
|
|||
onChange={handleChangeAndValidity}
|
||||
onInvalid={handleInvalid}
|
||||
/>
|
||||
{validationMessage && <FormText isError>{validationMessage}</FormText>}
|
||||
{validationMessage && (
|
||||
<FormText type="error">{validationMessage}</FormText>
|
||||
)}
|
||||
</OLFormGroup>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -196,18 +196,20 @@ function PasswordForm() {
|
|||
/>
|
||||
</OLFormGroup>
|
||||
) : null}
|
||||
<OLButton
|
||||
form="password-change-form"
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={!isFormValid}
|
||||
isLoading={isLoading}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('change'),
|
||||
}}
|
||||
>
|
||||
{t('change')}
|
||||
</OLButton>
|
||||
<OLFormGroup>
|
||||
<OLButton
|
||||
form="password-change-form"
|
||||
type="submit"
|
||||
variant="primary"
|
||||
disabled={!isFormValid}
|
||||
isLoading={isLoading}
|
||||
bs3Props={{
|
||||
loading: isLoading ? `${t('saving')}…` : t('change'),
|
||||
}}
|
||||
>
|
||||
{t('change')}
|
||||
</OLButton>
|
||||
</OLFormGroup>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -91,9 +91,13 @@ export function DropdownToggle({ ...props }: DropdownToggleProps) {
|
|||
return <BS5DropdownToggle {...props} />
|
||||
}
|
||||
|
||||
export function DropdownMenu({ as = 'ul', ...props }: DropdownMenuProps) {
|
||||
return <BS5DropdownMenu as={as} role="menu" {...props} />
|
||||
}
|
||||
export const DropdownMenu = forwardRef<
|
||||
typeof BS5DropdownMenu,
|
||||
DropdownMenuProps
|
||||
>(({ as = 'ul', ...props }, ref) => {
|
||||
return <BS5DropdownMenu as={as} role="menu" {...props} ref={ref} />
|
||||
})
|
||||
DropdownMenu.displayName = 'DropdownMenu'
|
||||
|
||||
export function DropdownDivider({ as = 'li' }: DropdownDividerProps) {
|
||||
return <BS5DropdownDivider as={as} />
|
||||
|
|
|
@ -36,7 +36,7 @@ const FormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
|
|||
)
|
||||
}
|
||||
|
||||
return <Form.Control className={className} {...props} />
|
||||
return <Form.Control ref={ref} className={className} {...props} />
|
||||
}
|
||||
)
|
||||
FormControl.displayName = 'FormControl'
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import { Form } from 'react-bootstrap-5'
|
||||
import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
|
||||
import { ComponentProps } from 'react'
|
||||
|
||||
export type FormFeedbackProps = Pick<
|
||||
ComponentProps<typeof Form.Control.Feedback>,
|
||||
'type' | 'className' | 'children'
|
||||
>
|
||||
|
||||
function FormFeedback(props: FormFeedbackProps) {
|
||||
return (
|
||||
<Form.Control.Feedback {...props}>
|
||||
<FormText type={props.type === 'invalid' ? 'error' : 'success'}>
|
||||
{props.children}
|
||||
</FormText>
|
||||
</Form.Control.Feedback>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormFeedback
|
|
@ -1,71 +1,49 @@
|
|||
import { Form } from 'react-bootstrap-5'
|
||||
import { MergeAndOverride } from '../../../../../../../types/utils'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import classnames from 'classnames'
|
||||
|
||||
type FormTextProps = MergeAndOverride<
|
||||
React.ComponentProps<(typeof Form)['Text']>,
|
||||
| {
|
||||
isInfo?: boolean
|
||||
isError?: never
|
||||
isWarning?: never
|
||||
isSuccess?: never
|
||||
}
|
||||
| {
|
||||
isInfo?: never
|
||||
isError?: boolean
|
||||
isWarning?: never
|
||||
isSuccess?: never
|
||||
}
|
||||
| {
|
||||
isInfo?: never
|
||||
isError?: never
|
||||
isWarning?: boolean
|
||||
isSuccess?: never
|
||||
}
|
||||
| {
|
||||
isInfo?: never
|
||||
isError?: never
|
||||
isWarning?: never
|
||||
isSuccess?: boolean
|
||||
}
|
||||
>
|
||||
type TextType = 'default' | 'info' | 'success' | 'warning' | 'error'
|
||||
|
||||
export const getFormTextColor = ({
|
||||
isError,
|
||||
isSuccess,
|
||||
isWarning,
|
||||
}: {
|
||||
isError?: boolean
|
||||
isSuccess?: boolean
|
||||
isWarning?: boolean
|
||||
}) => ({
|
||||
'text-danger': isError,
|
||||
'text-success': isSuccess,
|
||||
'text-warning': isWarning,
|
||||
})
|
||||
export type FormTextProps = React.ComponentProps<typeof Form.Text> & {
|
||||
type?: TextType
|
||||
}
|
||||
|
||||
const typeClassMap: Partial<Record<TextType, string>> = {
|
||||
error: 'text-danger',
|
||||
success: 'text-success',
|
||||
warning: 'text-warning',
|
||||
}
|
||||
|
||||
export const getFormTextClass = (type?: TextType) =>
|
||||
typeClassMap[type || 'default']
|
||||
|
||||
function FormTextIcon({ type }: { type?: TextType }) {
|
||||
switch (type) {
|
||||
case 'info':
|
||||
return <MaterialIcon type="info" className="text-info" />
|
||||
case 'success':
|
||||
return <MaterialIcon type="check_circle" />
|
||||
case 'warning':
|
||||
return <MaterialIcon type="warning" />
|
||||
case 'error':
|
||||
return <MaterialIcon type="error" />
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
function FormText({
|
||||
isInfo,
|
||||
isError,
|
||||
isWarning,
|
||||
isSuccess,
|
||||
type = 'default',
|
||||
children,
|
||||
className,
|
||||
...rest
|
||||
}: FormTextProps) {
|
||||
return (
|
||||
<Form.Text
|
||||
className={classnames(
|
||||
className,
|
||||
getFormTextColor({ isError, isSuccess, isWarning })
|
||||
)}
|
||||
className={classnames(className, getFormTextClass(type))}
|
||||
{...rest}
|
||||
>
|
||||
{isInfo && <MaterialIcon type="info" className="text-info" />}
|
||||
{isError && <MaterialIcon type="error" />}
|
||||
{isWarning && <MaterialIcon type="warning" />}
|
||||
{isSuccess && <MaterialIcon type="check_circle" />}
|
||||
<FormTextIcon type={type} />
|
||||
{children}
|
||||
</Form.Text>
|
||||
)
|
||||
|
|
|
@ -11,7 +11,7 @@ type OLFormCheckboxProps = React.ComponentProps<(typeof Form)['Check']> & {
|
|||
function OLFormCheckbox(props: OLFormCheckboxProps) {
|
||||
const { bs3Props, inputRef, ...rest } = props
|
||||
|
||||
const bs3FormLabelProps: React.ComponentProps<typeof BS3Checkbox> = {
|
||||
const bs3FormCheckboxProps: React.ComponentProps<typeof BS3Checkbox> = {
|
||||
children: rest.label,
|
||||
checked: rest.checked,
|
||||
required: rest.required,
|
||||
|
@ -34,9 +34,9 @@ function OLFormCheckbox(props: OLFormCheckboxProps) {
|
|||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
rest.type === 'radio' ? (
|
||||
<BS3Radio {...bs3FormLabelProps} />
|
||||
<BS3Radio {...bs3FormCheckboxProps} />
|
||||
) : (
|
||||
<BS3Checkbox {...bs3FormLabelProps} />
|
||||
<BS3Checkbox {...bs3FormCheckboxProps} />
|
||||
)
|
||||
}
|
||||
bs5={<Form.Check ref={inputRef} {...rest} />}
|
||||
|
|
|
@ -1,25 +1,29 @@
|
|||
import { forwardRef } from 'react'
|
||||
import { forwardRef, ComponentProps } from 'react'
|
||||
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
|
||||
import BS3FormControl from '@/features/ui/components/bootstrap-3/form/form-control'
|
||||
import FormControl from '@/features/ui/components/bootstrap-5/form/form-control'
|
||||
import BS3FormControl from '@/features/ui/components/bootstrap-3/form/form-control'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
|
||||
type OLFormControlProps = React.ComponentProps<typeof FormControl> & {
|
||||
type OLFormControlProps = ComponentProps<typeof FormControl> & {
|
||||
bs3Props?: Record<string, unknown>
|
||||
'data-ol-dirty'?: unknown
|
||||
}
|
||||
|
||||
type BS3FormControlProps = ComponentProps<typeof BS3FormControl>
|
||||
|
||||
const OLFormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
|
||||
(props, ref) => {
|
||||
const { bs3Props, ...rest } = props
|
||||
|
||||
let bs3FormControlProps: React.ComponentProps<typeof BS3FormControl> = {
|
||||
let bs3FormControlProps: BS3FormControlProps = {
|
||||
componentClass: rest.as,
|
||||
id: rest.id,
|
||||
name: rest.name,
|
||||
className: rest.className,
|
||||
style: rest.style,
|
||||
type: rest.type,
|
||||
value: rest.value,
|
||||
defaultValue: rest.defaultValue,
|
||||
required: rest.required,
|
||||
disabled: rest.disabled,
|
||||
placeholder: rest.placeholder,
|
||||
|
@ -28,10 +32,10 @@ const OLFormControl = forwardRef<HTMLInputElement, OLFormControlProps>(
|
|||
autoFocus: rest.autoFocus,
|
||||
minLength: rest.minLength,
|
||||
maxLength: rest.maxLength,
|
||||
onChange: rest.onChange as (e: React.ChangeEvent<unknown>) => void,
|
||||
onKeyDown: rest.onKeyDown as (e: React.KeyboardEvent<unknown>) => void,
|
||||
onFocus: rest.onFocus as (e: React.FocusEvent<unknown>) => void,
|
||||
onInvalid: rest.onInvalid as (e: React.InvalidEvent<unknown>) => void,
|
||||
onChange: rest.onChange as BS3FormControlProps['onChange'],
|
||||
onKeyDown: rest.onKeyDown as BS3FormControlProps['onKeyDown'],
|
||||
onFocus: rest.onFocus as BS3FormControlProps['onFocus'],
|
||||
onInvalid: rest.onInvalid as BS3FormControlProps['onInvalid'],
|
||||
inputRef: (inputElement: HTMLInputElement) => {
|
||||
if (typeof ref === 'function') {
|
||||
ref(inputElement)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
import { Form } from 'react-bootstrap-5'
|
||||
import {
|
||||
HelpBlock as BS3HelpBlock,
|
||||
HelpBlockProps as BS3HelpBlockProps,
|
||||
} from 'react-bootstrap'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
import { ComponentProps } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import FormFeedback from '@/features/ui/components/bootstrap-5/form/form-feedback'
|
||||
|
||||
type OLFormFeedbackProps = Pick<
|
||||
ComponentProps<typeof Form.Control.Feedback>,
|
||||
'type' | 'className' | 'children'
|
||||
> & {
|
||||
bs3Props?: Record<string, unknown>
|
||||
}
|
||||
|
||||
function OLFormFeedback(props: OLFormFeedbackProps) {
|
||||
const { bs3Props, children, ...bs5Props } = props
|
||||
|
||||
const bs3HelpBlockProps: BS3HelpBlockProps = {
|
||||
className: classnames(
|
||||
bs5Props.className,
|
||||
bs5Props.type === 'invalid' ? 'invalid-only' : null
|
||||
),
|
||||
children,
|
||||
...bs3Props,
|
||||
}
|
||||
|
||||
return (
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={<BS3HelpBlock {...bs3HelpBlockProps} />}
|
||||
bs5={<FormFeedback {...bs5Props}>{children}</FormFeedback>}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default OLFormFeedback
|
|
@ -1,5 +1,8 @@
|
|||
import { FormGroupProps } from 'react-bootstrap-5'
|
||||
import { FormGroup as BS3FormGroup } from 'react-bootstrap'
|
||||
import {
|
||||
FormGroup as BS3FormGroup,
|
||||
FormGroupProps as BS3FormGroupProps,
|
||||
} from 'react-bootstrap'
|
||||
import FormGroup from '@/features/ui/components/bootstrap-5/form/form-group'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
|
||||
|
@ -8,19 +11,19 @@ type OLFormGroupProps = FormGroupProps & {
|
|||
}
|
||||
|
||||
function OLFormGroup(props: OLFormGroupProps) {
|
||||
const { bs3Props, ...rest } = props
|
||||
const { bs3Props, className, ...rest } = props
|
||||
|
||||
const bs3FormGroupProps: React.ComponentProps<typeof BS3FormGroup> = {
|
||||
const bs3FormGroupProps: BS3FormGroupProps = {
|
||||
children: rest.children,
|
||||
controlId: rest.controlId,
|
||||
className: rest.className,
|
||||
className,
|
||||
...bs3Props,
|
||||
}
|
||||
|
||||
return (
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={<BS3FormGroup {...bs3FormGroupProps} />}
|
||||
bs5={<FormGroup {...rest} />}
|
||||
bs5={<FormGroup className={className} {...rest} />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
import { Form, FormSelectProps } from 'react-bootstrap-5'
|
||||
import {
|
||||
FormControl as BS3FormControl,
|
||||
FormControlProps as BS3FormControlProps,
|
||||
} from 'react-bootstrap'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
|
||||
|
||||
type OLFormSelectProps = FormSelectProps & {
|
||||
bs3Props?: Record<string, unknown>
|
||||
}
|
||||
|
||||
function OLFormSelect(props: OLFormSelectProps) {
|
||||
const { bs3Props, ...bs5Props } = props
|
||||
|
||||
const bs3FormSelectProps: BS3FormControlProps = {
|
||||
bsSize: bs5Props.size,
|
||||
name: bs5Props.name,
|
||||
value: bs5Props.value,
|
||||
disabled: bs5Props.disabled,
|
||||
onChange: bs5Props.onChange as BS3FormControlProps['onChange'],
|
||||
required: bs5Props.required,
|
||||
placeholder: bs5Props.placeholder,
|
||||
className: bs5Props.className,
|
||||
...bs3Props,
|
||||
}
|
||||
|
||||
// Get all `aria-*` and `data-*` attributes
|
||||
const extraProps = getAriaAndDataProps(bs5Props)
|
||||
|
||||
return (
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={
|
||||
<BS3FormControl
|
||||
componentClass="select"
|
||||
{...bs3FormSelectProps}
|
||||
{...extraProps}
|
||||
/>
|
||||
}
|
||||
bs5={<Form.Select {...bs5Props} />}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default OLFormSelect
|
|
@ -1,11 +1,12 @@
|
|||
import FormText, {
|
||||
getFormTextColor,
|
||||
FormTextProps,
|
||||
getFormTextClass,
|
||||
} from '@/features/ui/components/bootstrap-5/form/form-text'
|
||||
import PolymorphicComponent from '@/shared/components/polymorphic-component'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
import classnames from 'classnames'
|
||||
|
||||
type OLFormTextProps = React.ComponentProps<typeof FormText> & {
|
||||
type OLFormTextProps = FormTextProps & {
|
||||
bs3Props?: Record<string, unknown>
|
||||
}
|
||||
|
||||
|
@ -14,15 +15,7 @@ function OLFormText(props: OLFormTextProps) {
|
|||
|
||||
const bs3HelpBlockProps = {
|
||||
children: rest.children,
|
||||
className: classnames(
|
||||
'small',
|
||||
rest.className,
|
||||
getFormTextColor({
|
||||
isError: rest.isError,
|
||||
isSuccess: rest.isSuccess,
|
||||
isWarning: rest.isWarning,
|
||||
})
|
||||
),
|
||||
className: classnames('small', rest.className, getFormTextClass(rest.type)),
|
||||
as: 'span',
|
||||
...bs3Props,
|
||||
} as const satisfies React.ComponentProps<typeof PolymorphicComponent>
|
||||
|
|
|
@ -1,27 +1,42 @@
|
|||
import { Form } from 'react-bootstrap-5'
|
||||
import { Form as BS3Form } from 'react-bootstrap'
|
||||
import { Form as BS3Form, FormProps as BS3FormProps } from 'react-bootstrap'
|
||||
import BootstrapVersionSwitcher from '@/features/ui/components/bootstrap-5/bootstrap-version-switcher'
|
||||
import { ComponentProps } from 'react'
|
||||
import classnames from 'classnames'
|
||||
import { getAriaAndDataProps } from '@/features/utils/bootstrap-5'
|
||||
|
||||
type OLFormProps = React.ComponentProps<typeof Form> & {
|
||||
bs3Props?: React.ComponentProps<typeof BS3Form>
|
||||
type OLFormProps = ComponentProps<typeof Form> & {
|
||||
bs3Props?: ComponentProps<typeof BS3Form>
|
||||
}
|
||||
|
||||
function OLForm(props: OLFormProps) {
|
||||
const { bs3Props, ...rest } = props
|
||||
|
||||
const bs3FormProps: React.ComponentProps<typeof BS3Form> = {
|
||||
const bs3FormProps: BS3FormProps = {
|
||||
componentClass: rest.as,
|
||||
children: rest.children,
|
||||
id: rest.id,
|
||||
onSubmit: rest.onSubmit as React.FormEventHandler<BS3Form> | undefined,
|
||||
className: rest.className,
|
||||
onSubmit: rest.onSubmit as BS3FormProps['onSubmit'],
|
||||
onClick: rest.onClick as BS3FormProps['onClick'],
|
||||
name: rest.name,
|
||||
noValidate: rest.noValidate,
|
||||
role: rest.role,
|
||||
...bs3Props,
|
||||
}
|
||||
|
||||
const bs3ClassName = classnames(
|
||||
rest.className,
|
||||
rest.validated ? 'was-validated' : null
|
||||
)
|
||||
|
||||
// Get all `aria-*` and `data-*` attributes
|
||||
const extraProps = getAriaAndDataProps(rest)
|
||||
|
||||
return (
|
||||
<BootstrapVersionSwitcher
|
||||
bs3={<BS3Form {...bs3FormProps} />}
|
||||
bs3={
|
||||
<BS3Form className={bs3ClassName} {...bs3FormProps} {...extraProps} />
|
||||
}
|
||||
bs5={<Form {...rest} />}
|
||||
/>
|
||||
)
|
||||
|
|
|
@ -102,7 +102,7 @@ export function OLModalBody({ children, ...props }: OLModalBodyProps) {
|
|||
|
||||
const bs3ModalProps: BS3ModalBodyProps = {
|
||||
componentClass: bs5Props.as,
|
||||
bsClass: bs5Props.className,
|
||||
className: bs5Props.className,
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -118,7 +118,7 @@ export function OLModalFooter({ children, ...props }: OLModalFooterProps) {
|
|||
|
||||
const bs3ModalProps: BS3ModalFooterProps = {
|
||||
componentClass: bs5Props.as,
|
||||
bsClass: bs5Props.className,
|
||||
className: bs5Props.className,
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -21,7 +21,7 @@ export type DropdownProps = {
|
|||
export type DropdownItemProps = PropsWithChildren<{
|
||||
active?: boolean
|
||||
as?: ElementType
|
||||
description?: string
|
||||
description?: ReactNode
|
||||
disabled?: boolean
|
||||
eventKey?: string | number
|
||||
href?: string
|
||||
|
@ -32,6 +32,7 @@ export type DropdownItemProps = PropsWithChildren<{
|
|||
className?: string
|
||||
role?: string
|
||||
tabIndex?: number
|
||||
target?: string
|
||||
}>
|
||||
|
||||
export type DropdownToggleProps = PropsWithChildren<{
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import importOverleafModules from '../../../macros/import-overleaf-module.macro'
|
||||
import { JSXElementConstructor, useCallback, useState } from 'react'
|
||||
import { HelpSuggestionSearchProvider } from '../../../../modules/support/frontend/js/context/help-suggestion-search-context'
|
||||
|
||||
const [contactUsModalModules] = importOverleafModules('contactUsModal')
|
||||
const ContactUsModal: JSXElementConstructor<{
|
||||
show: boolean
|
||||
handleHide: () => void
|
||||
autofillProjectUrl: boolean
|
||||
}> = contactUsModalModules?.import.default
|
||||
|
||||
export const useContactUsModal = () => {
|
||||
export const useContactUsModal = (options = { autofillProjectUrl: true }) => {
|
||||
const [show, setShow] = useState(false)
|
||||
|
||||
const hideModal = useCallback((event?: Event) => {
|
||||
|
@ -21,7 +23,13 @@ export const useContactUsModal = () => {
|
|||
}, [])
|
||||
|
||||
const modal = ContactUsModal && (
|
||||
<ContactUsModal show={show} handleHide={hideModal} />
|
||||
<HelpSuggestionSearchProvider>
|
||||
<ContactUsModal
|
||||
show={show}
|
||||
handleHide={hideModal}
|
||||
autofillProjectUrl={options.autofillProjectUrl}
|
||||
/>
|
||||
</HelpSuggestionSearchProvider>
|
||||
)
|
||||
|
||||
return { modal, hideModal, showModal }
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
import { useState } from 'react'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
import ContactUsModal from '../../modules/support/frontend/js/components/contact-us-modal'
|
||||
import { ScopeDecorator } from './decorators/scope'
|
||||
|
||||
export const Generic = () => {
|
||||
const [show, setShow] = useState(true)
|
||||
|
||||
const handleHide = () => setShow(false)
|
||||
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.post('/support', { status: 200 }, { delay: 1000 })
|
||||
})
|
||||
|
||||
return <ContactUsModal show={show} handleHide={handleHide} />
|
||||
}
|
||||
|
||||
export const RequestError = args => {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.post('/support', { status: 404 }, { delay: 250 })
|
||||
})
|
||||
|
||||
return <ContactUsModal {...args} />
|
||||
}
|
||||
|
||||
export default {
|
||||
title: 'Shared / Modals / Contact Us',
|
||||
component: ContactUsModal,
|
||||
args: {
|
||||
show: true,
|
||||
handleHide: () => {},
|
||||
},
|
||||
argTypes: {
|
||||
handleHide: { action: 'close modal' },
|
||||
},
|
||||
decorators: [ScopeDecorator],
|
||||
}
|
93
services/web/frontend/stories/contact-us-modal.stories.tsx
Normal file
93
services/web/frontend/stories/contact-us-modal.stories.tsx
Normal file
|
@ -0,0 +1,93 @@
|
|||
import { ComponentProps } from 'react'
|
||||
import useFetchMock from './hooks/use-fetch-mock'
|
||||
import ContactUsModal from '../../modules/support/frontend/js/components/contact-us-modal'
|
||||
import { ScopeDecorator } from './decorators/scope'
|
||||
import { StoryObj } from '@storybook/react'
|
||||
import { FixedHelpSuggestionSearchProvider } from '../../modules/support/test/frontend/helpers/contact-us-modal-base-tests'
|
||||
|
||||
type Story = StoryObj<typeof ContactUsModal>
|
||||
type ContactUsModalProps = ComponentProps<typeof ContactUsModal>
|
||||
|
||||
function bootstrap3Story(render: Story['render']): Story {
|
||||
return {
|
||||
render,
|
||||
decorators: [
|
||||
story => {
|
||||
return ScopeDecorator(story)
|
||||
},
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
function bootstrap5Story(render: Story['render']): Story {
|
||||
return {
|
||||
render,
|
||||
decorators: [
|
||||
story => {
|
||||
return ScopeDecorator(story, undefined, {
|
||||
'ol-bootstrapVersion': 5,
|
||||
})
|
||||
},
|
||||
],
|
||||
parameters: {
|
||||
bootstrap5: true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
function GenericContactUsModal(args: ContactUsModalProps) {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.post('/support', { status: 200 }, { delay: 1000 })
|
||||
})
|
||||
|
||||
return (
|
||||
<FixedHelpSuggestionSearchProvider>
|
||||
<ContactUsModal {...args} />
|
||||
</FixedHelpSuggestionSearchProvider>
|
||||
)
|
||||
}
|
||||
|
||||
export const Generic: Story = bootstrap3Story(args => (
|
||||
<GenericContactUsModal {...args} />
|
||||
))
|
||||
|
||||
export const GenericBootstrap5: Story = bootstrap5Story(args => (
|
||||
<GenericContactUsModal {...args} />
|
||||
))
|
||||
|
||||
const ContactUsModalWithRequestError = (args: ContactUsModalProps) => {
|
||||
useFetchMock(fetchMock => {
|
||||
fetchMock.post('/support', { status: 404 }, { delay: 250 })
|
||||
})
|
||||
|
||||
return (
|
||||
<FixedHelpSuggestionSearchProvider>
|
||||
<ContactUsModal {...args} />
|
||||
</FixedHelpSuggestionSearchProvider>
|
||||
)
|
||||
}
|
||||
|
||||
const renderContactUsModalWithRequestError = (args: ContactUsModalProps) => (
|
||||
<ContactUsModalWithRequestError {...args} />
|
||||
)
|
||||
|
||||
export const RequestError: Story = bootstrap3Story(
|
||||
renderContactUsModalWithRequestError
|
||||
)
|
||||
|
||||
export const RequestErrorBootstrap5: Story = bootstrap5Story(
|
||||
renderContactUsModalWithRequestError
|
||||
)
|
||||
|
||||
export default {
|
||||
title: 'Shared / Modals / Contact Us',
|
||||
component: ContactUsModal,
|
||||
args: {
|
||||
show: true,
|
||||
handleHide: () => {},
|
||||
autofillProjectUrl: true,
|
||||
},
|
||||
argTypes: {
|
||||
handleHide: { action: 'close modal' },
|
||||
},
|
||||
}
|
|
@ -4,6 +4,7 @@ import FormGroup from '@/features/ui/components/bootstrap-5/form/form-group'
|
|||
import FormText from '@/features/ui/components/bootstrap-5/form/form-text'
|
||||
import FormControl from '@/features/ui/components/bootstrap-5/form/form-control'
|
||||
import MaterialIcon from '@/shared/components/material-icon'
|
||||
import FormFeedback from '@/features/ui/components/bootstrap-5/form/form-feedback'
|
||||
|
||||
const meta: Meta<React.ComponentProps<typeof FormControl>> = {
|
||||
title: 'Shared / Components / Bootstrap 5 / Form / Input',
|
||||
|
@ -57,7 +58,7 @@ export const Info: Story = {
|
|||
size="lg"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -67,7 +68,7 @@ export const Info: Story = {
|
|||
defaultValue="Regular input"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -78,7 +79,7 @@ export const Info: Story = {
|
|||
size="sm"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -98,7 +99,7 @@ export const Error: Story = {
|
|||
isInvalid
|
||||
{...args}
|
||||
/>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormFeedback type="invalid">Error</FormFeedback>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -109,7 +110,7 @@ export const Error: Story = {
|
|||
isInvalid
|
||||
{...args}
|
||||
/>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormFeedback type="invalid">Error</FormFeedback>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -121,7 +122,7 @@ export const Error: Story = {
|
|||
isInvalid
|
||||
{...args}
|
||||
/>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormFeedback type="invalid">Error</FormFeedback>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -140,7 +141,7 @@ export const Warning: Story = {
|
|||
size="lg"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -150,7 +151,7 @@ export const Warning: Story = {
|
|||
defaultValue="Regular input"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -161,7 +162,7 @@ export const Warning: Story = {
|
|||
size="sm"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -180,7 +181,7 @@ export const Success: Story = {
|
|||
size="lg"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -190,7 +191,7 @@ export const Success: Story = {
|
|||
defaultValue="Regular input"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -201,7 +202,7 @@ export const Success: Story = {
|
|||
size="sm"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -70,7 +70,7 @@ export const Info: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -81,7 +81,7 @@ export const Info: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -92,7 +92,7 @@ export const Info: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -111,7 +111,7 @@ export const Error: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormText type="error">Error</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -122,7 +122,7 @@ export const Error: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormText type="error">Error</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -133,7 +133,7 @@ export const Error: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormText type="error">Error</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -152,7 +152,7 @@ export const Warning: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -163,7 +163,7 @@ export const Warning: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -174,7 +174,7 @@ export const Warning: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -193,7 +193,7 @@ export const Success: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -204,7 +204,7 @@ export const Success: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -215,7 +215,7 @@ export const Success: Story = {
|
|||
<option value="2">Two</option>
|
||||
<option value="3">Three</option>
|
||||
</Form.Select>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -67,7 +67,7 @@ export const Info: Story = {
|
|||
size="lg"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -78,7 +78,7 @@ export const Info: Story = {
|
|||
defaultValue="Regular input"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -90,7 +90,7 @@ export const Info: Story = {
|
|||
size="sm"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isInfo>Info</FormText>
|
||||
<FormText type="info">Info</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -111,7 +111,7 @@ export const Error: Story = {
|
|||
isInvalid
|
||||
{...args}
|
||||
/>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormText type="error">Error</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -123,7 +123,7 @@ export const Error: Story = {
|
|||
isInvalid
|
||||
{...args}
|
||||
/>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormText type="error">Error</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -136,7 +136,7 @@ export const Error: Story = {
|
|||
isInvalid
|
||||
{...args}
|
||||
/>
|
||||
<FormText isError>Error</FormText>
|
||||
<FormText type="error">Error</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -156,7 +156,7 @@ export const Warning: Story = {
|
|||
size="lg"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -167,7 +167,7 @@ export const Warning: Story = {
|
|||
defaultValue="Regular input"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -179,7 +179,7 @@ export const Warning: Story = {
|
|||
size="sm"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isWarning>Warning</FormText>
|
||||
<FormText type="warning">Warning</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
@ -199,7 +199,7 @@ export const Success: Story = {
|
|||
size="lg"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-2">
|
||||
|
@ -210,7 +210,7 @@ export const Success: Story = {
|
|||
defaultValue="Regular input"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
<hr />
|
||||
<FormGroup controlId="id-3">
|
||||
|
@ -222,7 +222,7 @@ export const Success: Story = {
|
|||
size="sm"
|
||||
{...args}
|
||||
/>
|
||||
<FormText isSuccess>Success</FormText>
|
||||
<FormText type="success">Success</FormText>
|
||||
</FormGroup>
|
||||
</>
|
||||
)
|
||||
|
|
|
@ -120,6 +120,7 @@ $form-check-input-disabled-opacity: 1;
|
|||
// form-feedback-variables
|
||||
$form-feedback-invalid-color: $bg-danger-01;
|
||||
$form-feedback-icon-invalid: null;
|
||||
$form-feedback-margin-top: 0; // Our feedback component wraps Form.Text, which takes care of top margin
|
||||
|
||||
// form-validation-colors
|
||||
$form-invalid-color: $form-feedback-invalid-color;
|
||||
|
|
|
@ -3,3 +3,6 @@ $footer-height: 50px;
|
|||
|
||||
// Header
|
||||
$header-height: 68px;
|
||||
|
||||
// Forms
|
||||
$form-group-margin-bottom: $spacing-06;
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: var(--spacing-06);
|
||||
margin-bottom: $form-group-margin-bottom;
|
||||
}
|
||||
|
||||
.form-control-wrapper {
|
||||
|
|
|
@ -85,12 +85,3 @@
|
|||
.git-bridge-optional-tokens-actions {
|
||||
margin-top: var(--spacing-05);
|
||||
}
|
||||
|
||||
// Contact us modal
|
||||
.contact-us-modal-textarea {
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.modal-form-messages .notification {
|
||||
margin-bottom: var(--spacing-05);
|
||||
}
|
||||
|
|
|
@ -32,3 +32,9 @@ $is-overleaf-light: false;
|
|||
|
||||
// Page layout that isn't related to a particular component or page
|
||||
@import 'base/layout';
|
||||
|
||||
// Modals
|
||||
@import 'modals/all';
|
||||
|
||||
// Pages custom style
|
||||
@import 'pages/all';
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
@import 'contact-us-modal';
|
|
@ -0,0 +1,82 @@
|
|||
.contact-us-modal-textarea {
|
||||
height: 120px;
|
||||
}
|
||||
|
||||
.modal-form-messages .notification {
|
||||
margin-bottom: var(--spacing-05);
|
||||
}
|
||||
|
||||
.contact-suggestions {
|
||||
@include body-sm;
|
||||
|
||||
margin: 0 calc(-1 * var(--bs-modal-padding)) var(--spacing-05);
|
||||
padding: var(--spacing-05) 0;
|
||||
color: var(--content-secondary);
|
||||
background-color: var(--bg-light-secondary);
|
||||
border-top: solid 1px var(--border-primary-dark);
|
||||
border-bottom: solid 1px var(--border-primary-dark);
|
||||
}
|
||||
|
||||
.contact-suggestion-label {
|
||||
margin-bottom: var(--spacing-05);
|
||||
padding: 0 var(--spacing-07);
|
||||
}
|
||||
|
||||
.contact-suggestion-list {
|
||||
padding-left: 0;
|
||||
list-style: none;
|
||||
background-color: var(--white);
|
||||
border-top: solid 1px var(--border-primary-dark);
|
||||
border-bottom: solid 1px var(--border-primary-dark);
|
||||
margin: 0;
|
||||
|
||||
li:last-child .contact-suggestion-list-item {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.contact-suggestion-list-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: var(--content-secondary);
|
||||
padding: var(--spacing-05) var(--spacing-07);
|
||||
border-bottom: solid 1px var(--border-divider);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: var(--bg-light-secondary);
|
||||
|
||||
span {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.fa {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
|
||||
.fa {
|
||||
color: var(--neutral-30);
|
||||
}
|
||||
}
|
||||
|
||||
.contact-suggestions-dropdown {
|
||||
width: calc(100% - 2 * var(--bs-modal-padding));
|
||||
|
||||
.dropdown-header {
|
||||
@include body-sm;
|
||||
|
||||
padding: var(--bs-dropdown-item-padding-y) var(--bs-dropdown-item-padding-x);
|
||||
}
|
||||
|
||||
.dropdown-item {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.form-group + & {
|
||||
margin-top: $spacing-02 - $form-group-margin-bottom;
|
||||
}
|
||||
}
|
|
@ -357,6 +357,15 @@ input[type='checkbox'],
|
|||
margin-top: 5px;
|
||||
margin-bottom: 10px;
|
||||
color: lighten(@text-color, 25%); // lighten the text some for contrast
|
||||
|
||||
// Hide help blocks used as validation messages by default
|
||||
&.invalid-only {
|
||||
display: none;
|
||||
|
||||
.has-error & {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Inline forms
|
||||
|
|
|
@ -1455,6 +1455,8 @@
|
|||
"please_enter_email": "Please enter your email address",
|
||||
"please_get_in_touch": "Please get in touch",
|
||||
"please_link_before_making_primary": "Please confirm your email by linking to your institutional account before making it the primary email.",
|
||||
"please_provide_a_message": "Please provide a message",
|
||||
"please_provide_a_subject": "Please provide a subject",
|
||||
"please_reconfirm_institutional_email": "Please take a moment to confirm your institutional email address or <0>remove it</0> from your account.",
|
||||
"please_reconfirm_your_affiliation_before_making_this_primary": "Please confirm your affiliation before making this the primary.",
|
||||
"please_refresh": "Please refresh the page to continue.",
|
||||
|
|
Loading…
Reference in a new issue