Skip to content

Commit

Permalink
fix: multi-lang all up view refinement (#3847)
Browse files Browse the repository at this point in the history
* search box for multilang adding

* pretty tablelist column space

* fine-grain updateTemplate

* when blur submit updated value

* refine api

* editable tablelist

* eable multiline

* update test

* adjust column width

Co-authored-by: Chris Whitten <christopher.whitten@microsoft.com>
  • Loading branch information
zhixzhan and cwhitten committed Aug 18, 2020
1 parent b12756e commit 1e09cb6
Show file tree
Hide file tree
Showing 10 changed files with 461 additions and 114 deletions.
123 changes: 123 additions & 0 deletions Composer/packages/client/src/components/EditableField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

import React, { useState, useEffect } from 'react';
import { TextField, ITextFieldStyles, ITextFieldProps } from 'office-ui-fabric-react/lib/TextField';
import { NeutralColors } from '@uifabric/fluent-theme';
import { mergeStyleSets } from '@uifabric/styling';

interface EditableFieldProps extends Omit<ITextFieldProps, 'onChange' | 'onFocus' | 'onBlur'> {
fontSize?: string;
styles?: Partial<ITextFieldStyles>;
transparentBorder?: boolean;
ariaLabel?: string;
error?: string | JSX.Element;

className?: string;
depth: number;
description?: string;
disabled?: boolean;
id: string;
name: string;
placeholder?: string;
readonly?: boolean;
required?: boolean;
value?: string;

onChange: (newValue?: string) => void;
onFocus?: (id: string, value?: string) => void;
onBlur?: (id: string, value?: string) => void;
}

const EditableField: React.FC<EditableFieldProps> = (props) => {
const {
depth,
styles = {},
placeholder,
fontSize,
multiline = false,
onChange,
onBlur,
value,
id,
error,
className,
transparentBorder,
ariaLabel,
} = props;
const [editing, setEditing] = useState<boolean>(false);
const [hasFocus, setHasFocus] = useState<boolean>(false);
const [localValue, setLocalValue] = useState<string | undefined>(value);
const [hasBeenEdited, setHasBeenEdited] = useState<boolean>(false);

useEffect(() => {
if (!hasBeenEdited || value !== localValue) {
setLocalValue(value);
}
}, [value]);

const handleChange = (_e: any, newValue?: string) => {
setLocalValue(newValue);
setHasBeenEdited(true);
onChange(newValue);
};

const handleCommit = () => {
setHasFocus(false);
setEditing(false);
onBlur && onBlur(id, localValue);
};

let borderColor: string | undefined = undefined;

if (!editing && !error) {
borderColor = localValue || transparentBorder || depth > 1 ? 'transparent' : NeutralColors.gray30;
}

return (
<div
className={className}
onMouseEnter={() => setEditing(true)}
onMouseLeave={() => !hasFocus && setEditing(false)}
>
<TextField
ariaLabel={ariaLabel}
autoComplete="off"
errorMessage={error as string}
multiline={multiline}
placeholder={placeholder || value}
styles={
mergeStyleSets(
{
root: { margin: '0', width: '100%' },
field: {
fontSize: fontSize,
selectors: {
'::placeholder': {
fontSize: fontSize,
},
},
},
fieldGroup: {
borderColor,
transition: 'border-color 0.1s linear',
selectors: {
':hover': {
borderColor: hasFocus ? undefined : NeutralColors.gray30,
},
},
},
},
styles
) as Partial<ITextFieldStyles>
}
value={localValue}
onBlur={handleCommit}
onChange={handleChange}
onFocus={() => setHasFocus(true)}
/>
</div>
);
};

export { EditableField };
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { jsx } from '@emotion/core';
import React, { useState, useCallback, useMemo, useEffect } from 'react';
import formatMessage from 'format-message';
import { PrimaryButton, DefaultButton } from 'office-ui-fabric-react/lib/Button';
import { SearchBox } from 'office-ui-fabric-react/lib/SearchBox';
import { ScrollablePane, IScrollablePaneStyles } from 'office-ui-fabric-react/lib/ScrollablePane';
import { Stack, StackItem } from 'office-ui-fabric-react/lib/Stack';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
Expand Down Expand Up @@ -39,6 +40,7 @@ const AddLanguageModal: React.FC<IAddLanguageModalProps> = (props) => {
switchTo: false,
};
const [formData, setFormData] = useState<ILanguageFormData>(initialFormData);
const [searchKeywords, setSearchKeywords] = useState('');

useEffect(() => {
setFormData({ ...formData, defaultLang: defaultLanguage });
Expand Down Expand Up @@ -94,27 +96,29 @@ const AddLanguageModal: React.FC<IAddLanguageModalProps> = (props) => {

const formTitles = { ...MultiLanguagesDialog.ADD_DIALOG };

const languageCheckBoxList = languageListTemplatesSorted(currentLanguages, locale, defaultLanguage).map((item) => {
const { language, isEnabled, isCurrent, isDefault, locale } = item;
let label = language;
if (isDefault) {
label += formatMessage(' - Original');
}
if (isCurrent) {
label += formatMessage(' - Current');
}
return (
<Checkbox
key={locale}
className={classNames.checkboxItem}
defaultChecked={isEnabled}
disabled={isEnabled}
label={label}
title={locale}
onChange={onChange(locale)}
/>
);
});
const languageCheckBoxList = languageListTemplatesSorted(currentLanguages, locale, defaultLanguage)
.filter((item) => item.language.toLowerCase().includes(searchKeywords.toLowerCase()))
.map((item) => {
const { language, isEnabled, isCurrent, isDefault, locale } = item;
let label = language;
if (isDefault) {
label += formatMessage(' - Original');
}
if (isCurrent) {
label += formatMessage(' - Current');
}
return (
<Checkbox
key={locale}
className={classNames.checkboxItem}
defaultChecked={isEnabled}
disabled={isEnabled}
label={label}
title={locale}
onChange={onChange(locale)}
/>
);
});

const defalutLanguageListOptions = useMemo(() => {
const languageList = languageListTemplates(currentLanguages, locale, defaultLanguage);
Expand All @@ -129,6 +133,10 @@ const AddLanguageModal: React.FC<IAddLanguageModalProps> = (props) => {
});
}, [currentLanguages]);

const onSearch = (_e, newValue) => {
setSearchKeywords(newValue.trim());
};

const scrollablePaneStyles: Partial<IScrollablePaneStyles> = { root: classNames.pane };

return (
Expand All @@ -149,6 +157,12 @@ const AddLanguageModal: React.FC<IAddLanguageModalProps> = (props) => {
</StackItem>
<StackItem grow={0}>
<Label>{MultiLanguagesDialog.ADD_DIALOG.selectionTitle}</Label>
<SearchBox
disableAnimation
placeholder={MultiLanguagesDialog.ADD_DIALOG.searchPlaceHolder}
styles={{ root: { width: 300 } }}
onChange={onSearch}
/>
<ScrollablePane styles={scrollablePaneStyles}>{languageCheckBoxList}</ScrollablePane>
</StackItem>
<StackItem>
Expand Down
1 change: 1 addition & 0 deletions Composer/packages/client/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ export const MultiLanguagesDialog = {
'This language will be copied and used as the basis (and fallback language) for the translation.'
),
selectionTitle: formatMessage('To which language will you be translating your bot?'),
searchPlaceHolder: formatMessage('Search'),
whenDoneText: formatMessage(
'When done, switch to the newly created language and start the (manual) translation process.'
),
Expand Down
Loading

0 comments on commit 1e09cb6

Please sign in to comment.