From 5cbfcf5f3bc4cdbc77754c33c0600de53ff055ef Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ivar=20Conradi=20=C3=98sthus?=
Date: Wed, 20 May 2020 16:32:29 +0200
Subject: [PATCH] fix: improve on variant ui
---
frontend/src/__mocks__/react-mdl.js | 5 +
frontend/src/component/common/util.js | 10 +
.../update-variant-component-test.jsx.snap | 806 ++++++++++++++----
.../update-variant-component-test.jsx | 52 +-
.../component/feature/variant/add-variant.jsx | 242 ++++++
.../feature/variant/override-config.jsx | 47 +
.../variant/update-variant-component.jsx | 197 ++---
.../variant/update-variant-container.jsx | 87 +-
.../variant/variant-edit-component.jsx | 162 ----
.../variant/variant-view-component.jsx | 14 +-
.../component/feature/variant/variant.scss | 37 +
11 files changed, 1101 insertions(+), 558 deletions(-)
create mode 100644 frontend/src/component/feature/variant/add-variant.jsx
create mode 100644 frontend/src/component/feature/variant/override-config.jsx
delete mode 100644 frontend/src/component/feature/variant/variant-edit-component.jsx
diff --git a/frontend/src/__mocks__/react-mdl.js b/frontend/src/__mocks__/react-mdl.js
index fc109db7a9..e525a380e7 100644
--- a/frontend/src/__mocks__/react-mdl.js
+++ b/frontend/src/__mocks__/react-mdl.js
@@ -29,4 +29,9 @@ module.exports = {
FooterDropDownSection: 'react-mdl-FooterDropDownSection',
FooterSection: 'react-mdl-FooterSection',
FooterLinkList: 'react-mdl-FooterLinkList',
+ Tooltip: 'react-mdl-Tooltip',
+ Dialog: 'react-mdl-Dialog',
+ DialogTitle: 'react-mdl-DialogTitle',
+ DialogContent: 'react-mdl-DialogContent',
+ DialogActions: 'react-mdl-DialogActions',
};
diff --git a/frontend/src/component/common/util.js b/frontend/src/component/common/util.js
index 429e267869..872c4f0c6f 100644
--- a/frontend/src/component/common/util.js
+++ b/frontend/src/component/common/util.js
@@ -20,3 +20,13 @@ export const trim = value => {
return value;
}
};
+
+export function updateWeight(variants, totalWeight) {
+ const size = variants.length;
+ const percentage = parseInt((1 / size) * totalWeight);
+
+ variants.forEach(v => {
+ v.weight = percentage;
+ });
+ return variants;
+}
diff --git a/frontend/src/component/feature/variant/__tests__/__snapshots__/update-variant-component-test.jsx.snap b/frontend/src/component/feature/variant/__tests__/__snapshots__/update-variant-component-test.jsx.snap
index bc47c361fb..bc610fa8d0 100644
--- a/frontend/src/component/feature/variant/__tests__/__snapshots__/update-variant-component-test.jsx.snap
+++ b/frontend/src/component/feature/variant/__tests__/__snapshots__/update-variant-component-test.jsx.snap
@@ -4,6 +4,7 @@ exports[`renders correctly with with variants 1`] = `
getVariant()
- method in the client SDK.
+ method in the Client SDK.
-
- The sum of variants weights needs to be a constant number to guarantee consistent hashing in the client implementations, this is why we will sometime allocate a few more percentages to the first variant if the sum is not exactly 100. In a final version of this feature it should be possible to the user to manually set the percentages for each variant.
-
+
+
+
+ Variant name
+ |
+ |
+
+ Weight
+ |
+ |
+
+
+
+
+
+ blue
+ |
+
+
+
+ Overrides
+
+ |
+
+ 3.4
+ %
+ |
+
+
+
+ |
+
+
+
+ yellow
+ |
+
+
+ |
+
+ 3.3
+ %
+ |
+
+
+
+ |
+
+
+
+ orange
+ |
+
+
+ Payload
+
+
+ |
+
+ 3.3
+ %
+ |
+
+
+
+ |
+
+
+
+
-
`;
@@ -158,6 +320,7 @@ exports[`renders correctly with without variants 1`] = `
getVariant()
- method in the client SDK.
+ method in the Client SDK.
-
- The sum of variants weights needs to be a constant number to guarantee consistent hashing in the client implementations, this is why we will sometime allocate a few more percentages to the first variant if the sum is not exactly 100. In a final version of this feature it should be possible to the user to manually set the percentages for each variant.
-
+
+
+
+ Variant name
+ |
+ |
+
+ Weight
+ |
+ |
+
+
+
+
+
-
`;
@@ -228,6 +539,7 @@ exports[`renders correctly with without variants and no permissions 1`] = `
getVariant()
- method in the client SDK.
+ method in the Client SDK.
-
- The sum of variants weights needs to be a constant number to guarantee consistent hashing in the client implementations, this is why we will sometime allocate a few more percentages to the first variant if the sum is not exactly 100. In a final version of this feature it should be possible to the user to manually set the percentages for each variant.
-
-
`;
diff --git a/frontend/src/component/feature/variant/__tests__/update-variant-component-test.jsx b/frontend/src/component/feature/variant/__tests__/update-variant-component-test.jsx
index ab242df171..cad73143e1 100644
--- a/frontend/src/component/feature/variant/__tests__/update-variant-component-test.jsx
+++ b/frontend/src/component/feature/variant/__tests__/update-variant-component-test.jsx
@@ -8,34 +8,14 @@ import { UPDATE_FEATURE } from '../../../../permissions';
jest.mock('react-mdl');
test('renders correctly with without variants', () => {
- const featureToggle = {
- name: 'Another',
- description: "another's description",
- enabled: false,
- strategies: [
- {
- name: 'gradualRolloutRandom',
- parameters: {
- percentage: 50,
- },
- },
- ],
- createdAt: '2018-02-04T20:27:52.127Z',
- };
const tree = renderer.create(
permission === UPDATE_FEATURE}
/>
@@ -45,34 +25,14 @@ test('renders correctly with without variants', () => {
});
test('renders correctly with without variants and no permissions', () => {
- const featureToggle = {
- name: 'Another',
- description: "another's description",
- enabled: false,
- strategies: [
- {
- name: 'gradualRolloutRandom',
- parameters: {
- percentage: 50,
- },
- },
- ],
- createdAt: '2018-02-04T20:27:52.127Z',
- };
const tree = renderer.create(
false}
/>
@@ -124,16 +84,10 @@ test('renders correctly with with variants', () => {
permission === UPDATE_FEATURE}
/>
diff --git a/frontend/src/component/feature/variant/add-variant.jsx b/frontend/src/component/feature/variant/add-variant.jsx
new file mode 100644
index 0000000000..21da1973da
--- /dev/null
+++ b/frontend/src/component/feature/variant/add-variant.jsx
@@ -0,0 +1,242 @@
+import React, { useState, useEffect } from 'react';
+import PropTypes from 'prop-types';
+import {
+ Button,
+ Textfield,
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Grid,
+ Cell,
+ Tooltip,
+ Icon,
+} from 'react-mdl';
+import MySelect from '../form/select';
+import { trim } from '../form/util';
+import styles from './variant.scss';
+import OverrideConfig from './override-config';
+
+const payloadOptions = [
+ { key: 'string', label: 'string' },
+ { key: 'json', label: 'json' },
+ { key: 'csv', label: 'csv' },
+];
+
+const EMPTY_PAYLOAD = { type: 'string', value: '' };
+
+function AddVariant({ showDialog, closeDialog, save, validateName, editVariant, title }) {
+ const [data, setData] = useState({});
+ const [payload, setPayload] = useState(EMPTY_PAYLOAD);
+ const [overrides, setOverrides] = useState([]);
+ const [error, setError] = useState({});
+
+ const clear = () => {
+ if (editVariant) {
+ setData({ name: editVariant.name });
+ if (editVariant.payload) {
+ setPayload(editVariant.payload);
+ }
+ if (editVariant.overrides) {
+ setOverrides(
+ editVariant.overrides.map(o => ({ contextName: o.contextName, values: o.values.join(', ') }))
+ );
+ } else {
+ setOverrides([]);
+ }
+ } else {
+ setData({});
+ setPayload(EMPTY_PAYLOAD);
+ setOverrides([]);
+ }
+ setError({});
+ };
+
+ useEffect(() => {
+ clear();
+ }, [editVariant]);
+
+ const setName = e => {
+ setData({
+ ...data,
+ [e.target.name]: trim(e.target.value),
+ });
+ };
+
+ const submit = async e => {
+ e.preventDefault();
+
+ const validationError = validateName(data.name);
+
+ if (validationError) {
+ setError(validationError);
+ return;
+ }
+
+ try {
+ const variant = {
+ name: data.name,
+ payload: payload.value ? payload : undefined,
+ overrides: overrides
+ .map(o => ({
+ contextName: o.contextName,
+ values: o.values
+ .split(',')
+ .map(v => v.trim())
+ .filter(v => v),
+ }))
+ .filter(o => o.values && o.values.length > 0),
+ };
+ await save(variant);
+ clear();
+ closeDialog();
+ } catch (error) {
+ const msg = error.error || 'Could not add variant';
+ setError({ general: msg });
+ }
+ };
+
+ const onPayload = e => {
+ e.preventDefault();
+ setPayload({
+ ...payload,
+ [e.target.name]: e.target.value,
+ });
+ };
+
+ const onCancel = e => {
+ e.preventDefault();
+ clear();
+ closeDialog();
+ };
+
+ const updateOverrideOption = index => e => {
+ e.preventDefault();
+ setOverrides(
+ overrides.map((o, i) => {
+ if (i === index) {
+ o[e.target.name] = e.target.value;
+ }
+ return o;
+ })
+ );
+ };
+
+ const removeOverrideOption = index => e => {
+ e.preventDefault();
+ setOverrides(overrides.filter((o, i) => i !== index));
+ };
+
+ const onAddOverride = e => {
+ e.preventDefault();
+ setOverrides([...overrides, ...[{ contextName: 'userId', values: '' }]]);
+ };
+
+ return (
+
+ );
+}
+
+AddVariant.propTypes = {
+ showDialog: PropTypes.bool.isRequired,
+ closeDialog: PropTypes.func.isRequired,
+ save: PropTypes.func.isRequired,
+ validateName: PropTypes.func.isRequired,
+ editVariant: PropTypes.object,
+ title: PropTypes.string,
+};
+
+export default AddVariant;
diff --git a/frontend/src/component/feature/variant/override-config.jsx b/frontend/src/component/feature/variant/override-config.jsx
new file mode 100644
index 0000000000..91d45c9c8a
--- /dev/null
+++ b/frontend/src/component/feature/variant/override-config.jsx
@@ -0,0 +1,47 @@
+import React from 'react';
+import PropTypes from 'prop-types';
+import { Textfield, Grid, Cell, IconButton } from 'react-mdl';
+import MySelect from '../form/select';
+
+const overrideOptions = [
+ { key: 'userId', label: 'userId' },
+ { key: 'appName', label: 'appName' },
+];
+
+function OverrideConfig({ overrides, updateOverrideOption, removeOverrideOption }) {
+ return overrides.map((o, i) => (
+
+
+
+ |
+
+
+ |
+
+
+ |
+
+ ));
+}
+
+OverrideConfig.propTypes = {
+ overrides: PropTypes.array.isRequired,
+ updateOverrideOption: PropTypes.func.isRequired,
+ removeOverrideOption: PropTypes.func.isRequired,
+};
+
+export default OverrideConfig;
diff --git a/frontend/src/component/feature/variant/update-variant-component.jsx b/frontend/src/component/feature/variant/update-variant-component.jsx
index 3b61e04ea1..a777a58ed6 100644
--- a/frontend/src/component/feature/variant/update-variant-component.jsx
+++ b/frontend/src/component/feature/variant/update-variant-component.jsx
@@ -1,169 +1,120 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
-import { FormButtons } from '../../common';
import VariantViewComponent from './variant-view-component';
-import VariantEditComponent from './variant-edit-component';
import styles from './variant.scss';
import { UPDATE_FEATURE } from '../../../permissions';
+import AddVariant from './add-variant';
+
+const initalState = { showDialog: false, editVariant: undefined, editIndex: -1 };
class UpdateVariantComponent extends Component {
constructor(props) {
super(props);
+ this.state = { ...initalState };
}
- // eslint-disable-next-line camelcase
- UNSAFE_componentWillMount() {
- // TODO unwind this stuff
- if (this.props.initCallRequired === true) {
- this.props.init(this.props.input);
- }
- }
-
- updateWeight(newWeight, newSize) {
- const variants = this.props.input.variants || [];
- variants.forEach((v, i) => {
- v.weight = newWeight;
- this.props.updateVariant(i, v);
- });
-
- // Make sure the sum of weigths is 100.
- const sum = newWeight * newSize;
- if (sum !== 100) {
- const first = variants[0];
- first.weight = 100 - sum + first.weight;
- this.props.updateVariant(0, first);
- }
- }
-
- addVariant = (e, variants) => {
- e.preventDefault();
- const size = variants.length + 1;
- const percentage = parseInt((1 / size) * 100);
- const variant = {
- name: '',
- weight: percentage,
- edit: true,
- };
-
- this.updateWeight(percentage, size);
- this.props.addVariant(variant);
+ closeDialog = () => {
+ this.setState({ ...initalState });
};
- removeVariant = (e, index) => {
+ openAddVariant = e => {
+ e.preventDefault();
+ this.setState({
+ showDialog: true,
+ editVariant: undefined,
+ editIndex: undefined,
+ title: 'Add variant',
+ });
+ };
+
+ openEditVariant = (e, index, variant) => {
+ e.preventDefault();
+ if (this.props.hasPermission(UPDATE_FEATURE)) {
+ this.setState({
+ showDialog: true,
+ editVariant: variant,
+ editIndex: index,
+ title: 'Edit variant',
+ });
+ }
+ };
+
+ validateName = name => {
+ if (!name) {
+ return { name: 'Name is required' };
+ }
+ };
+
+ onRemoveVariant = (e, index) => {
e.preventDefault();
- const variants = this.props.input.variants;
- const size = variants.length - 1;
- const percentage = parseInt((1 / size) * 100);
- this.updateWeight(percentage, size);
this.props.removeVariant(index);
};
- editVariant = (e, index, v) => {
- e.preventDefault();
- if (this.props.hasPermission(UPDATE_FEATURE)) {
- v.edit = true;
- this.props.updateVariant(index, v);
- }
- };
+ renderVariant = (variant, index) => (
+ this.openEditVariant(e, index, variant)}
+ removeVariant={e => this.onRemoveVariant(e, index)}
+ hasPermission={this.props.hasPermission}
+ />
+ );
- closeVariant = (e, index, v) => {
- e.preventDefault();
- v.edit = false;
- this.props.updateVariant(index, v);
- };
-
- updateVariant = (index, newVariant) => {
- this.props.updateVariant(index, newVariant);
- };
-
- renderVariant = (variant, index) =>
- variant.edit ? (
- this.removeVariant(e, index)}
- closeVariant={e => this.closeVariant(e, index, variant)}
- updateVariant={this.updateVariant.bind(this, index)}
- />
- ) : (
- this.editVariant(e, index, variant)}
- removeVariant={e => this.removeVariant(e, index)}
- hasPermission={this.props.hasPermission}
- />
- );
-
- renderVariants = variants => {
- if (variants.length > 0) {
- return (
-
-
-
- Name |
- Weight |
- |
-
-
- {variants.map(this.renderVariant)}
-
- );
- } else {
- return ;
- }
- };
+ renderVariants = variants => (
+
+
+
+ Variant name |
+ |
+ Weight |
+ |
+
+
+ {variants.map(this.renderVariant)}
+
+ );
render() {
- const { onSubmit, onCancel, input, features } = this.props;
- const variants = input.variants || [];
+ const { showDialog, editVariant, editIndex, title } = this.state;
+ const { variants, addVariant, updateVariant } = this.props;
+ const saveVariant = editVariant ? updateVariant.bind(null, editIndex) : addVariant;
return (
-
+
Variants allows you to return a variant object if the feature toggle is considered enabled for the
current request. When using variants you should use the{' '}
- getVariant()
method in the client SDK.
-
-
- The sum of variants weights needs to be a constant number to guarantee consistent hashing in the
- client implementations, this is why we will sometime allocate a few more percentages to the first
- variant if the sum is not exactly 100. In a final version of this feature it should be possible to
- the user to manually set the percentages for each variant.
+ getVariant()
method in the Client SDK.
+ {this.renderVariants(variants)}
+
{this.props.hasPermission(UPDATE_FEATURE) ? (
- this.addVariant(e, variants)}>
+
Add variant
) : null}
-
-
+
);
}
}
UpdateVariantComponent.propTypes = {
- input: PropTypes.object,
- features: PropTypes.array,
- setValue: PropTypes.func.isRequired,
+ variants: PropTypes.array.isRequired,
addVariant: PropTypes.func.isRequired,
removeVariant: PropTypes.func.isRequired,
updateVariant: PropTypes.func.isRequired,
- onSubmit: PropTypes.func.isRequired,
- onCancel: PropTypes.func.isRequired,
- initCallRequired: PropTypes.bool,
- init: PropTypes.func,
hasPermission: PropTypes.func.isRequired,
};
diff --git a/frontend/src/component/feature/variant/update-variant-container.jsx b/frontend/src/component/feature/variant/update-variant-container.jsx
index 14733bc012..9a584458a3 100644
--- a/frontend/src/component/feature/variant/update-variant-container.jsx
+++ b/frontend/src/component/feature/variant/update-variant-container.jsx
@@ -1,67 +1,36 @@
import { connect } from 'react-redux';
import { requestUpdateFeatureToggleVariants } from '../../../store/feature-actions';
-import { createMapper, createActions } from '../../input-helpers';
import UpdateFeatureToggleComponent from './update-variant-component';
+import { updateWeight } from '../../common/util';
-const ID = 'edit-toggle-variants';
-function getId(props) {
- return [ID, props.featureToggle.name];
-}
-// TODO: need to scope to the active featureToggle
-// best is to emulate the "input-storage"?
-const mapStateToProps = createMapper({
- id: getId,
- getDefault: (state, ownProps) => ownProps.featureToggle,
- prepare: props => {
- props.editmode = true;
- return props;
+const mapStateToProps = (state, ownProps) => ({
+ variants: ownProps.featureToggle.variants || [],
+ hasPermission: ownProps.hasPermission,
+});
+
+const mapDispatchToProps = (dispatch, ownProps) => ({
+ addVariant: variant => {
+ const { featureToggle } = ownProps;
+ const currentVariants = featureToggle.variants || [];
+ const variants = [...currentVariants, variant];
+ updateWeight(variants, 1000);
+ return requestUpdateFeatureToggleVariants(featureToggle, variants)(dispatch);
+ },
+ removeVariant: index => {
+ const { featureToggle } = ownProps;
+ const currentVariants = featureToggle.variants || [];
+ const variants = currentVariants.filter((v, i) => i !== index);
+ updateWeight(variants, 1000);
+ requestUpdateFeatureToggleVariants(featureToggle, variants)(dispatch);
+ },
+ updateVariant: (index, variant) => {
+ const { featureToggle } = ownProps;
+ const currentVariants = featureToggle.variants || [];
+ const variants = currentVariants.map((v, i) => (i === index ? variant : v));
+ updateWeight(variants, 1000);
+ requestUpdateFeatureToggleVariants(featureToggle, variants)(dispatch);
},
});
-const prepare = (methods, dispatch, ownProps) => {
- methods.onSubmit = (input, features) => e => {
- e.preventDefault();
-
- const featureToggle = features.find(f => f.name === input.name);
-
- // Kind of a hack
- featureToggle.strategies.forEach(s => (s.id = undefined));
-
- const variants = input.variants.map(v => {
- delete v.edit;
- return v;
- });
-
- requestUpdateFeatureToggleVariants(featureToggle, variants)(dispatch);
- variants.forEach((v, i) => methods.updateInList('variants', i, v));
- };
-
- methods.onCancel = evt => {
- evt.preventDefault();
- ownProps.history.push(`/features/view/${ownProps.featureToggle.name}`);
- };
-
- methods.addVariant = v => {
- methods.pushToList('variants', v);
- };
-
- methods.removeVariant = index => {
- methods.removeFromList('variants', index);
- };
-
- methods.updateVariant = (index, n) => {
- methods.updateInList('variants', index, n);
- };
-
- methods.validateName = () => {};
-
- return methods;
-};
-
-const actions = createActions({
- id: getId,
- prepare,
-});
-
-export default connect(mapStateToProps, actions)(UpdateFeatureToggleComponent);
+export default connect(mapStateToProps, mapDispatchToProps)(UpdateFeatureToggleComponent);
diff --git a/frontend/src/component/feature/variant/variant-edit-component.jsx b/frontend/src/component/feature/variant/variant-edit-component.jsx
deleted file mode 100644
index d6925e1fa5..0000000000
--- a/frontend/src/component/feature/variant/variant-edit-component.jsx
+++ /dev/null
@@ -1,162 +0,0 @@
-import React, { Component } from 'react';
-import PropTypes from 'prop-types';
-
-import { IconButton, Cell, Grid, Textfield, Tooltip, Icon } from 'react-mdl';
-import styles from './variant.scss';
-
-class VariantEditComponent extends Component {
- constructor(props) {
- super(props);
- }
-
- componentDidMount() {
- this.refs.name.inputRef.focus();
- }
-
- getUserIdOverrides(variant) {
- const overrides = variant.overrides || [];
- const userIdOverrides = overrides.find(o => o.contextName === 'userId') || { values: [] };
- return userIdOverrides.values.join(', ');
- }
-
- toggleEditMode = e => {
- e.preventDefault();
- this.setState({
- editmode: !this.state.editmode,
- });
- };
-
- updateToggleName = e => {
- e.preventDefault();
- const variant = this.props.variant;
- variant.name = e.target.value;
- this.props.updateVariant(variant);
- };
-
- updatePayload = e => {
- e.preventDefault();
- const variant = this.props.variant;
- variant.payload = {
- type: 'string',
- value: e.target.value,
- };
- this.props.updateVariant(variant);
- };
-
- updateOverrides = (contextName, e) => {
- e.preventDefault();
- const values = e.target.value.split(',').map(v => v.trim());
- const variant = this.props.variant;
-
- // Clean empty string. (should be moved to action)
- if (values.length === 1 && !values[0]) {
- variant.overrides = undefined;
- } else {
- variant.overrides = [{ contextName, values }];
- }
- this.props.updateVariant(variant);
- };
-
- render() {
- const { variant, closeVariant, removeVariant } = this.props;
- const payload = variant.payload ? variant.payload.value : '';
- const userIdOverrides = this.getUserIdOverrides(variant);
-
- return (
-
-
-
-
-
- |
- |
-
-
-
-
- |
-
-
- Passed to the variant object.
- Can be anything (json, value, csv)
-
- }
- >
-
-
- |
-
-
-
-
- |
-
-
- Here you can specify which users that
- should get this variant.
-
- }
- >
-
-
- |
-
-
- Close
-
- |
-
- {}}
- />
- |
-
-
-
- |
-
- );
- }
-}
-
-VariantEditComponent.propTypes = {
- variant: PropTypes.object,
- removeVariant: PropTypes.func,
- updateVariant: PropTypes.func,
- closeVariant: PropTypes.func,
-};
-
-export default VariantEditComponent;
diff --git a/frontend/src/component/feature/variant/variant-view-component.jsx b/frontend/src/component/feature/variant/variant-view-component.jsx
index 2d0ad8b0e1..8206261ab7 100644
--- a/frontend/src/component/feature/variant/variant-view-component.jsx
+++ b/frontend/src/component/feature/variant/variant-view-component.jsx
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
-import { IconButton } from 'react-mdl';
+import { IconButton, Chip } from 'react-mdl';
import styles from './variant.scss';
import { UPDATE_FEATURE } from '../../../permissions';
@@ -9,10 +9,18 @@ function VariantViewComponent({ variant, editVariant, removeVariant, hasPermissi
return (
{variant.name} |
- {variant.weight} |
+
+ {variant.payload ? Payload : undefined}{' '}
+ {variant.overrides && variant.overrides.length > 0 ? (
+ Overrides
+ ) : (
+ undefined
+ )}
+ |
+ {variant.weight / 10.0} % |
{hasPermission(UPDATE_FEATURE) ? (
-
+
|
) : (
diff --git a/frontend/src/component/feature/variant/variant.scss b/frontend/src/component/feature/variant/variant.scss
index 720301c34e..9097083a92 100644
--- a/frontend/src/component/feature/variant/variant.scss
+++ b/frontend/src/component/feature/variant/variant.scss
@@ -1,5 +1,6 @@
.variantTable {
width: 100%;
+ max-width: 700px;
th, td {
text-align: center;
@@ -14,6 +15,24 @@
}
}
+@media (max-width: 600px) {
+ th.labels {
+ display: none;
+ }
+ td.labels {
+ display: none;
+ }
+}
+
+th.labels {
+ text-align: right;
+}
+
+td.labels {
+ text-align: right;
+ vertical-align: top;
+}
+
th.actions {
text-align: right;
}
@@ -22,3 +41,21 @@ td.actions {
text-align: right;
vertical-align: top;
}
+
+.modal {
+ max-width: 90%;
+ width: 600px;
+ position: absolute !important;
+}
+
+@media (max-width: 600px) {
+ .modal {
+ top: 0 !important;
+ }
+}
+
+.tooltip {
+ i {
+ font-size: 18px;
+ }
+}
\ No newline at end of file