Стиль материала и пользовательского интерфейса не применяется в производстве, но прекрасно применяется в местном масштабе. - PullRequest
1 голос
/ 21 апреля 2020

Итак, у нас есть компонент search select, который отлично выглядит в dev, но в тот момент, когда он запускается в производство, он ломается. Я пробовал перемещать стилирование по-разному, но, похоже, ничего не работает, а только ломает ввод. Кто-нибудь имел опыт работы с этой проблемой и как ее исправить?

Здесь локально:

enter image description here enter image description here

А вот как это выглядит в производстве:

enter image description here enter image description here

Вот используемые компоненты:

SearchSelect. js

import PropTypes from "prop-types";
import NoSsr from "@material-ui/core/NoSsr";

import Async from "react-select/lib/Async";
import debounce from "debounce-promise";
import { withStyles } from "@material-ui/core";

import { styles, useStyles } from "./Styles";
import * as Components from "./Components";

const comps = { ...Components };
const SearchSelect = ({
  optionsFetcher,
  isMulti,
  minLength,
  delay,
  maxOptions,
  label,
  errors,
  onChange,
  onBlur,
  classes: customClasses,
  value,
  showErrors,
  components,
}) => {
  const classes = useStyles();
  const [menuIsOpen, setMenuIsOpen] = useState(false);
  const [maxReached, setMaxReached] = useState(false);

  const debounced = debounce(val => {
    if (minLength && val.length <= minLength) {
      setMenuIsOpen(false);
      return Promise.resolve([]);
    }
    setMenuIsOpen(true);
    return optionsFetcher(val);
  }, delay);

  const handleChange = (selectedOptions, { action }) => {
    if (maxReached && action !== "remove-value" && action !== "clear") {
      return;
    }

    if (selectedOptions && selectedOptions.length <= maxOptions) {
      setMaxReached(false);
    }

    if (maxOptions && isMulti && action === "select-option") {
      if (selectedOptions.length === Number(maxOptions)) {
        setMaxReached(true);
      }
      setMenuIsOpen(false);
    } else {
      setMenuIsOpen(false);
    }

    onChange(selectedOptions);
  };

  const noOptionsHelper = input => {
    if (input === "") {
      return "Please type to search...";
    }
    return `No options matching "${input}"`;
  };

  const noOptionsMessage = ({ inputValue }) => {
    if (maxReached) {
      return `You can only select ${maxOptions} options.`;
    }
    return noOptionsHelper(inputValue);
  };

  return (
    <div className={classes.root}>
      <NoSsr>
        <Async
          classes={classes}
          dropdownIndicator={{
            hasValue: false,
          }}
          inputId="react-select-single"
          TextFieldProps={{
            label,
            InputLabelProps: {
              htmlFor: "react-select-single",
              shrink: "true",
              classes: {
                root: customClasses.cssLabel,
                focused: customClasses.cssFocused,
              },
            },
            InputProps: {
              value: value.label || value,
              classes: {
                root: customClasses.cssOutlinedInput,
                focused: customClasses.cssFocused,
                notchedOutline: customClasses.notchedOutline,
                disabled: customClasses.disabled,
              },
            },
          }}
          error={(showErrors && errors && Boolean(errors.length)) || false}
          helperText={errors}
          placeholder=""
          components={{
            ...components,
            ...comps,
            DropdownIndicator: () => null,
            IndicatorSeparator: () => null,
          }}
          isClearable
          onChange={handleChange}
          onBlur={e => {
            setMenuIsOpen(false);
            onBlur(e);
          }}
          cacheOptions={false}
          loadOptions={val => debounced(val)}
          isMulti={isMulti}
          menuIsOpen={menuIsOpen}
          closeMenuOnSelect
          blurInputOnSelect={false}
          noOptionsMessage={noOptionsMessage}
          styles={{
            input: () => ({ height: "50px", paddingTop: "13px" }),
          }}
          value={value}
        />
      </NoSsr>
    </div>
  );
};

SearchSelect.defaultProps = {
  isMulti: false,
  minLength: 0,
  delay: 150,
  maxOptions: null,
  showErrors: true,
  errors: null,
  components: {},
};

SearchSelect.propTypes = {
  optionsFetcher: PropTypes.func.isRequired,
  isMulti: PropTypes.bool,
  minLength: PropTypes.number,
  delay: PropTypes.number,
  maxOptions: PropTypes.number,
  label: PropTypes.string.isRequired,
  showErrors: PropTypes.bool,
  errors: PropTypes.arrayOf(PropTypes.any),
  onChange: PropTypes.func.isRequired,
  onBlur: PropTypes.func.isRequired,
  classes: PropTypes.objectOf(PropTypes.any).isRequired,
  value: PropTypes.objectOf(PropTypes.any).isRequired,
  components: PropTypes.objectOf(PropTypes.any),
};

export default withStyles(styles)(SearchSelect);

Компоненты. js

/**
 * All code taken and customised
 * https://v3.material-ui.com/demos/autocomplete/
 * 'react-select' section
 */
import React from "react";
import clsx from "clsx";
import Typography from "@material-ui/core/Typography";
import TextField from "@material-ui/core/TextField";
import Paper from "@material-ui/core/Paper";
import Chip from "@material-ui/core/Chip";
import CancelIcon from "@material-ui/icons/Cancel";
import PropTypes from "prop-types";
import { useStyles } from "./Styles";

export const NoOptionsMessage = ({ selectProps, innerProps, children }) => (
  <Typography
    color="textSecondary"
    className={selectProps.classes.noOptionsMessage}
    {...innerProps}
  >
    {children}
  </Typography>
);
NoOptionsMessage.propTypes = {
  selectProps: PropTypes.objectOf(PropTypes.any).isRequired,
  innerProps: PropTypes.objectOf(PropTypes.any).isRequired,
  children: PropTypes.node.isRequired,
};

export const inputComponent = ({ inputRef, ...props }) => <div ref={inputRef} {...props} />;
inputComponent.propTypes = { inputRef: PropTypes.string.isRequired };

export const Control = props => {
  const {
    children,
    innerProps,
    innerRef,
    selectProps: { TextFieldProps, error, helperText },
  } = props;

  const classes = useStyles();

  return (
    <TextField
      variant="outlined"
      fullWidth
      error={error}
      helperText={helperText}
      {...TextFieldProps}
      InputProps={{
        inputComponent,
        inputProps: {
          className: classes.input,
          ref: innerRef,
          children,
          ...innerProps,
        },
        ...TextFieldProps.InputProps,
      }}
    />
  );
};
Control.propTypes = {
  children: PropTypes.node.isRequired,
  innerProps: PropTypes.objectOf(PropTypes.any).isRequired,
  innerRef: PropTypes.string.isRequired,
  classes: PropTypes.objectOf(PropTypes.any).isRequired,
  TextFieldProps: PropTypes.objectOf(PropTypes.any).isRequired,
  error: PropTypes.bool.isRequired,
  helperText: PropTypes.oneOfType([PropTypes.array, PropTypes.string]).isRequired,
};

export const Placeholder = props => {
  const { selectProps, innerProps = {}, children } = props;
  return (
    <Typography color="textSecondary" className={selectProps.classes.placeholder} {...innerProps}>
      {children}
    </Typography>
  );
};
Placeholder.propTypes = {
  selectProps: PropTypes.objectOf(PropTypes.any).isRequired,
  innerProps: PropTypes.objectOf(PropTypes.any).isRequired,
  children: PropTypes.node.isRequired,
};

export const SingleValue = ({ selectProps, innerProps, children }) => (
  <Typography className={selectProps.classes.singleValue} {...innerProps}>
    {children}
  </Typography>
);
SingleValue.propTypes = {
  selectProps: PropTypes.objectOf(PropTypes.any).isRequired,
  innerProps: PropTypes.objectOf(PropTypes.any).isRequired,
  children: PropTypes.node.isRequired,
};

export const ValueContainer = ({ selectProps, children }) => (
  <div className={selectProps.classes.valueContainer}>{children}</div>
);
ValueContainer.propTypes = {
  selectProps: PropTypes.objectOf(PropTypes.any).isRequired,
  children: PropTypes.node.isRequired,
};

export const MultiValue = ({ children, selectProps, isFocused, removeProps }) => (
  <Chip
    tabIndex={-1}
    label={children}
    className={clsx(selectProps.classes.chip, {
      [selectProps.classes.chipFocused]: isFocused,
    })}
    onDelete={removeProps.onClick}
    deleteIcon={<CancelIcon {...removeProps} />}
  />
);
MultiValue.propTypes = {
  selectProps: PropTypes.objectOf(PropTypes.any).isRequired,
  children: PropTypes.node.isRequired,
  isFocused: PropTypes.bool.isRequired,
  removeProps: PropTypes.func.isRequired,
};

export const Menu = ({ selectProps, innerProps, children }) => (
  <Paper square className={selectProps.classes.paper} {...innerProps}>
    {children}
  </Paper>
);
Menu.propTypes = {
  selectProps: PropTypes.objectOf(PropTypes.any).isRequired,
  innerProps: PropTypes.objectOf(PropTypes.any).isRequired,
  children: PropTypes.node.isRequired,
};

Стили. js

import { emphasize, makeStyles } from "@material-ui/core/styles";

/**
 * Custom styles to override material UI & react-selects
 * styles
 */
export const styles = {
  cssLabel: {
    color: "#333",
    fontSize: "12px",
    fontWeight: "light",
    zIndex: 3,
  },
  textField: {
    width: "100%",
    margin: "10px 0",
  },

  cssOutlinedInput: {
    "&$cssFocused $notchedOutline": {
      borderColor: "#333 !important",
      borderRadius: "8px",
      borderWidth: "1.5px",
      zIndex: 2,
    },
  },
  inputTypeSearch: {
    padding: "16px 14px",
  },
  disabled: {
    "&$disabled": {
      cursor: "not-allowed",
      backgroundColor: "#ebebeb",
      borderRadius: "11px",
    },
  },
  cssFocused: {
    "&$cssLabel": {
      color: "#333",
    },
  },
  notchedOutline: {
    border: "1px solid #DBDADA",
    borderRadius: "11px",
    zIndex: 2,
  },
};

/**
 * Code taken from
 * https://v3.material-ui.com/demos/autocomplete/
 * 'react-select' section
 */
export const useStyles = makeStyles(theme => ({
  root: {
    flexGrow: 1,
    minWidth: 290,
    marginBottom: 12,
    marginTop: 12,
    zIndex: 2,
  },

  input: {
    display: "flex",
    padding: 0,
    height: "auto",
    paddingLeft: "14px",
    zIndex: 2,
    background: "#fff",
  },

  valueContainer: {
    display: "flex",
    flexWrap: "wrap",
    flex: 2,
    alignItems: "center",
  },
  chip: {
    margin: theme.spacing(0.5, 0.25),
  },
  chipFocused: {
    backgroundColor: emphasize(
      theme.palette.type === "light" ? theme.palette.grey[300] : theme.palette.grey[700],
      0.08,
    ),
  },
  noOptionsMessage: {
    padding: theme.spacing(1, 2),
  },
  singleValue: {
    fontSize: 16,
  },
  placeholder: {
    position: "absolute",
    left: 14,
    bottom: 11,
    fontSize: 14,
  },
  paper: {
    background: "#F4F4F4",
    border: "none",
    boxShadow: "none",

    position: "absolute",
    top: "30px",
    left: "-4%",

    padding: "50px 0",

    width: "107%",

    zIndex: 1,
  },
  divider: {
    height: theme.spacing(2),
  },
}));
...