@octopusdeploy/design-system-components
Version:
The design systems component library.
210 lines (209 loc) • 9.34 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.Text = void 0;
const jsx_runtime_1 = require("react/jsx-runtime");
const css_1 = require("@emotion/css");
const TextField_1 = __importDefault(require("@mui/material/TextField"));
const design_system_tokens_1 = require("@octopusdeploy/design-system-tokens");
const react_1 = __importDefault(require("react"));
const uuid_1 = require("uuid");
/* @deprecated
* Deprecated: 15/09/2025
* This component is deprecated.
* As part of our effort to improve form usability and accessibility, please migrate to TextField where feasible.
* We recognize TextField has different styling; if that prevents an immediate swap, you may continue using this
* component until all the new form components are in place.
*/
class Text extends react_1.default.Component {
static defaultProps = {
type: "text",
autoComplete: "off",
autoFocus: false,
applyMaxWidth: false,
multiline: false,
showBorder: false,
maxRows: 30,
maxLength: undefined,
textInputRef: (input) => {
/* Do nothing */
},
};
textFieldInput = undefined;
genericName;
uniqueId;
constructor(props) {
super(props);
this.state = {
showExternalError: true,
};
this.genericName = props.generateUniqueName ? `input-${(0, uuid_1.v4)()}` : typeof props.label === "string" ? props.label : props.placeholder ? props.placeholder : `input-${(0, uuid_1.v4)()}`;
this.uniqueId = `${props.label}-${(0, uuid_1.v4)()}`;
}
// eslint-disable-next-line react/no-unsafe
UNSAFE_componentWillMount() {
if (this.props.textInputRef) {
this.props.textInputRef(this);
}
}
componentWillUnmount() {
if (this.props.textInputRef) {
this.props.textInputRef(null);
}
}
// eslint-disable-next-line react/no-unsafe
UNSAFE_componentWillReceiveProps(nextProps) {
const isNewExternalErrorAvailable = nextProps.error !== this.props.error;
if (isNewExternalErrorAvailable) {
this.setState({ showExternalError: true });
}
}
getSelection = () => {
const input = this.textFieldInput;
return { start: input?.selectionStart ?? null, end: input?.selectionEnd ?? null };
};
setValueAndSelection = (selection, value) => {
const input = this.textFieldInput;
if (!input)
return;
input.value = value;
input.selectionStart = selection.start;
input.selectionEnd = selection.end;
this.callValidateAndChange(input.value);
};
isFocused() {
return this.textFieldInput !== undefined && this.textFieldInput === document.activeElement;
}
select() {
if (this.textFieldInput) {
this.textFieldInput.select();
}
}
focus() {
if (this.textFieldInput) {
this.textFieldInput.focus();
}
}
blur() {
if (this.textFieldInput) {
this.textFieldInput.blur();
}
}
insertAtCursor(value) {
if (!this.textFieldInput) {
return;
}
const input = this.textFieldInput;
if (input.selectionStart || input.selectionStart === 0) {
const startPos = input.selectionStart;
const endPos = input.selectionEnd;
if (endPos !== null) {
input.value = input.value.substring(0, startPos) + value + input.value.substring(endPos, input.value.length);
}
input.selectionStart = startPos + value.length;
input.selectionEnd = startPos + value.length;
}
else {
input.value += value;
}
this.callValidateAndChange(input.value);
}
handleChange = (event) => {
event.preventDefault();
event.stopPropagation();
const value = event.currentTarget.value;
this.callValidateAndChange(value);
};
callValidateAndChange = (value) => {
if (this.props.validate) {
const result = this.props.validate(value);
this.setState({ error: result });
if (this.props.onValidate) {
this.props.onValidate(result);
}
}
this.setState({ showExternalError: false });
if (this.props.onChange) {
this.props.onChange(value);
}
};
handleKeyPress = (event) => {
if (this.props.onKeyPress) {
this.props.onKeyPress(event);
}
};
handleKeyDown = (event) => {
if (this.props.onKeyDown) {
this.props.onKeyDown(event);
}
};
render() {
const { id, validate, error, onChange, onValidate, value, label, min, max, name, maxLength, placeholder, hideUnderline, warning, monoSpacedFont, onKeyPress, minRows, multiline, applyMaxWidth, showBorder, textInputRef, inputContainerRef, showValueAsTitleAttribute, autoComplete, type, usePlaceholderAsLabel, accessibleName, customMargins, generateUniqueName, helperText, margin, inputProps, ...otherProps } = this.props;
const err = this.state.error || (this.state.showExternalError && error);
const errorText = err || warning;
const val = value ? value : "";
// Play carefully here. There's some interesting combinations happening with SensitiveInput label/placeholder etc. We have tests around this.
const inputLabel = usePlaceholderAsLabel && placeholder && !val ? placeholder : label;
const commonStyle = { margin: customMargins ? customMargins : !inputLabel ? "1rem 0" : "0 0 1rem 0", pointerEvents: "auto" }; // This allows us to match the padding/alignment of our other select components (AutoComplete, Select, MultiSelect), so we get consistent alignments.
const widthStyle = applyMaxWidth ? { maxWidth: "100%" } : {};
const inputId = id || this.uniqueId;
const getHelpText = () => {
if (errorText)
return errorText;
if (helperText)
return helperText;
return null;
};
const typeComplete = type === "password" && autoComplete === "off" ? "new-password" : autoComplete;
return ((0, jsx_runtime_1.jsx)("div", { className: (0, css_1.cx)(styles.container, { [styles.border]: showBorder }), children: (0, jsx_runtime_1.jsx)(TextField_1.default, { ref: inputContainerRef, id: inputId, className: (0, css_1.cx)(styles.text, { [styles.noUnderline]: hideUnderline }), inputRef: (textFieldInput) => {
this.textFieldInput = textFieldInput;
}, variant: "standard", type: type, autoComplete: typeComplete, autoCorrect: autoComplete, placeholder: placeholder, title: showValueAsTitleAttribute ? val : undefined, name: name || this.genericName, value: val, label: inputLabel, onChange: this.handleChange, error: !!errorText, helperText: getHelpText(), FormHelperTextProps: { component: "div" }, inputProps: { min: min, max: max, "aria-label": accessibleName }, InputProps: {
...this.props.inputProps,
classes: {
...this.props.inputProps?.classes,
input: (0, css_1.cx)(this.props.inputProps?.classes?.input, { [styles.monospacedText]: monoSpacedFont }),
},
}, minRows: multiline ? minRows || 3 : minRows, multiline: multiline, onKeyPress: this.handleKeyPress, onKeyDown: this.handleKeyDown, style: { ...widthStyle, ...commonStyle }, margin: margin, ...otherProps }) }));
}
}
exports.Text = Text;
const muiInputClasses = {
input: "MuiInputBase-input",
};
const styles = {
container: (0, css_1.css)({
width: "100%",
}),
border: (0, css_1.css)({
padding: `0 ${design_system_tokens_1.space[8]} 0 ${design_system_tokens_1.space[8]}`,
border: `${design_system_tokens_1.borderWidth[1]} solid ${design_system_tokens_1.themeTokens.color.border.primary}`,
marginBottom: design_system_tokens_1.space[8],
}),
text: (0, css_1.css)({
verticalAlign: "baseline !important",
width: "100% !important",
minWidth: "0 !important",
}),
monospacedText: (0, css_1.css)({
[`&.${muiInputClasses.input}`]: {
font: design_system_tokens_1.text.code.regular.medium,
},
}),
noUnderline: (0, css_1.css)({
borderBottom: 0,
"div::before": {
// This isn't great because it relies on the structure of MUI's TextField component, but it's the only way I could find to override this here.
borderBottom: 0,
"div::before": {
// This isn't great because it relies on the structure of MUI's TextField component, but it's the only way I could find to override this here.
borderBottom: 0,
},
"div::after": {
// This isn't great because it relies on the structure of MUI's TextField component, but it's the only way I could find to override this here.
borderBottom: 0,
},
},
}),
};