Skip to content

Commit

Permalink
feat: Rename inline edit (V1) (#2881)
Browse files Browse the repository at this point in the history
* feat: rename InlineEdit

* chore: remove incorrect commentry

---------

Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com>
  • Loading branch information
lee-chase and kodiakhq[bot] authored Apr 26, 2023
1 parent 121dbf1 commit cff465d
Show file tree
Hide file tree
Showing 11 changed files with 404 additions and 6 deletions.
42 changes: 42 additions & 0 deletions packages/cloud-cognitive/src/components/EditInPlace/EditInPlace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* Copyright IBM Corp. 2022, 2022
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, { forwardRef } from 'react';
import PropTypes from 'prop-types';
import { pkg } from '../../settings';
import { InlineEditV1 } from '../InlineEditV1';
import { InlineEditV2 } from '../InlineEditV2';

/**
* this is a wrapper component that will allow us to support the rename of InlineEdit to EditInPlace
*
* if the user passes in the v2 feature flag the v2 version of the component will be rendered
* since this is a temporary solution the named export should just remain InlineEdit unlike how
* Card works as a base layer for Productive and Expressive cards.
*/

const componentName = 'EditInPlace';

export let EditInPlace = forwardRef(({ v2, ...rest }, ref) => {
const props = {
...rest,
ref,
};
if (v2 === true) {
return <InlineEditV2 {...props} />;
}

return <InlineEditV1 {...props} />;
});

EditInPlace = pkg.checkComponentEnabled(EditInPlace, componentName);

EditInPlace.displayName = componentName;

EditInPlace.propTypes = {
v2: PropTypes.bool,
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
/**
* Copyright IBM Corp. 2022, 2022
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, { useEffect, useState } from 'react';
import { action } from '@storybook/addon-actions';

import {
getStoryTitle,
prepareStory,
} from '../../global/js/utils/story-helper';
import { DisplayBox } from '../../global/js/utils/DisplayBox';

import { EditInPlace } from '.';

import mdx from '../InlineEditV1//InlineEditV1.mdx';

import styles from '../InlineEditV1/_storybook-styles.scss';

const storyClass = 'inline-edit-stories';

const validationOptions = {
'Default, no validation change': {},
'On change or save invalid if empty': { onChangeInvalidIfEmpty: true },
'On change or save includes ABC invalid': { onChangeInvalidWithABC: true },
'On save invalid if empty': { onSaveInvalidIfEmpty: true },
'On save includes ABC invalid': { onSaveInvalidWithABC: true },
};

const buttonTooltipAlignmentOptions = {
'Default / undefined': undefined,
'All start': 'start',
'All center': 'center',
'All end': 'end',
'Edit start, Cancel center, save end': {
edit: 'start',
cancel: 'center',
save: 'end',
},
};

const buttonTooltipPositionOptions = {
'Default / undefined': undefined,
'All top': 'top',
'All right': 'right',
'All bottom': 'bottom',
'All left': 'left',
'Edit and save right, cancel left': {
edit: 'right',
cancel: 'left',
save: 'right',
},
};

export default {
title: getStoryTitle(EditInPlace.displayName),
component: EditInPlace,
argTypes: {
buttonTooltipAlignment: {
control: {
type: 'select',
labels: Object.keys(buttonTooltipAlignmentOptions),
},
options: Object.values(buttonTooltipAlignmentOptions).map((_k, i) => i),
mapping: Object.values(buttonTooltipAlignmentOptions),
},
buttonTooltipPosition: {
control: {
type: 'select',
labels: Object.keys(buttonTooltipPositionOptions),
},
options: Object.values(buttonTooltipPositionOptions).map((_k, i) => i),
mapping: Object.values(buttonTooltipPositionOptions),
},
containerWidth: {
control: { type: 'range', min: 20, max: 800, step: 10 },
description:
'Controls containing element width. Used for demonstration purposes, not property of the component.',
},
EditInPlaceFullWidth: {
control: { type: 'boolean' },
description:
'Sets component width `100%`. Used for demonstration purposes, not property of the component.',
},
validation: {
control: {
type: 'select',
labels: Object.keys(validationOptions),
},
options: Object.values(validationOptions).map((_k, i) => i),
mapping: Object.values(validationOptions),
defaultValue: 0,
},
},
parameters: {
styles,
layout: 'padded',
docs: {
page: mdx,
},
},
decorators: [
(story) => (
<DisplayBox className={`${storyClass}__viewport`}>{story()}</DisplayBox>
),
],
};

const actionSave = action('save');
const actionRejectSave = action('rejected save');
const actionChange = action('change');
const actionRejectChange = action('rejected change');
const actionCancel = action('cancel');

/**
* TODO: Declare template(s) for one or more scenarios.
*/
const Template = ({
containerWidth,
EditInPlaceFullWidth,
invalid,
invalidText,
editDescription,
cancelDescription,
saveDescription,
validation,
value: initialValue,
...rest
}) => {
const [value, setValue] = useState(initialValue);
const [liveInvalid, setLiveInvalid] = useState(invalid);
const [liveInvalidText, setLiveInvalidText] = useState(invalidText);

useEffect(() => {
setLiveInvalid(invalid);
}, [invalid]);

const handleValidation = (val, change, save) => {
let newInvalid = false;
let invalidText = '';
let updateValidation = false;

const zeroLength = val.length === 0;
const hasABC = /ABC/.test(val);

if (
(change && validation?.onChangeInvalidIfEmpty) ||
(save && validation?.onSaveInvalidIfEmpty)
) {
newInvalid = zeroLength;
invalidText = newInvalid ? 'This field cannot be empty' : '';
updateValidation = true;
} else if (
(change && validation?.onChangeInvalidWithABC) ||
(save && validation?.onSaveInvalidWithABC)
) {
newInvalid = hasABC;
invalidText = newInvalid ? 'Cannot contain ABC in the entry' : '';
updateValidation = true;
}

if (updateValidation) {
setLiveInvalid(newInvalid);
setLiveInvalidText(invalidText);
}
return updateValidation && newInvalid;
};

const onSave = (val) => {
const reject = handleValidation(val, false, true);

if (reject) {
actionRejectSave(val);
} else {
setValue(val);
actionSave(val);
}
};
const onChange = (val) => {
const reject = handleValidation(val, true, false);

if (reject) {
actionRejectChange(val);
} else {
actionChange(val);
}
};
const onCancel = actionCancel;

useEffect(() => {
setValue(initialValue);
}, [initialValue]);

return (
<div
style={{ width: containerWidth }}
className={EditInPlaceFullWidth ? 'component-full-width' : ''}
>
<EditInPlace
{...rest}
{...{
editDescription,
invalid: liveInvalid,
invalidText: liveInvalidText,
onSave,
onChange,
onCancel,
cancelDescription,
saveDescription,
value,
}}
/>
</div>
);
};

export const Version1 = prepareStory(Template, {
args: {
containerWidth: 300,
EditInPlaceFullWidth: true,
editDescription: 'Edit',
id: 'edit button',
labelText: 'Inline edit',
cancelDescription: 'Cancel',
saveDescription: 'Save',
value: 'hello, world',
placeholder: 'placeholder text',
},
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
/**
* Copyright IBM Corp. 2022, 2022
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, { useState } from 'react';
import {
getStoryTitle,
prepareStory,
} from '../../global/js/utils/story-helper';
import { action } from '@storybook/addon-actions';
import { EditInPlace } from '.';
import mdx from '../InlineEditV2/InlineEditV2.mdx';
import styles from '../InlineEditV2/_storybook-styles.scss';

export default {
title: getStoryTitle(EditInPlace.displayName),
component: EditInPlace,
parameters: {
styles,
docs: {
page: mdx,
},
},
};

const actionSave = action('save');
const actionChange = action('change');
const actionCancel = action('cancel');

const defaultProps = {
cancelLabel: 'Cancel',
editLabel: 'Edit',
id: 'story-id',
invalid: false,
invalidLabel: 'This field is required',
labelText: 'Label text',
onCancel: () => {},
onChange: () => {},
onSave: () => {},
// readOnly: false,
// readOnlyLabel: 'This value is read only',
saveLabel: 'Save',
v2: true,
value: 'default',
};

const Template = (args) => {
const [value, setValue] = useState(defaultProps.value);

const onChange = (val) => {
setValue(val);
actionChange(val);
};

const onSave = () => {
console.log('saved!', value);
actionSave(value);
};

const onCancel = (initialVal) => {
setValue(initialVal);
actionCancel(initialVal);
};

const props = {
...args,
value,
onChange,
onSave,
onCancel,
};

return <EditInPlace {...props} className="inline-edit-v2-example" />;
};

export const Version2 = prepareStory(Template, {
args: {
...defaultProps,
},
});

// export const ReadOnly = prepareStory(Template, {
// args: {
// ...defaultProps,
// readOnly: true,
// },
// });
8 changes: 8 additions & 0 deletions packages/cloud-cognitive/src/components/EditInPlace/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
/**
* Copyright IBM Corp. 2022, 2022
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/

export { EditInPlace } from './EditInPlace';
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,13 @@ export let InlineEdit = forwardRef(({ v2, ...rest }, ref) => {
return <InlineEditV1 {...props} />;
});

InlineEdit = pkg.checkComponentEnabled(InlineEdit, componentName);
InlineEdit.deprecated = {
details: `The InlineEdit component is being renamed to EditInPlace in the next major version of @carbon/ibm-products. You can prepare by updating your usage of InlineEdit to EditInPlace.`,
level: 'warn',
};

InlineEdit.displayName = componentName;
InlineEdit = pkg.checkComponentEnabled(InlineEdit, componentName);

InlineEdit.propTypes = {
v2: PropTypes.bool,
Expand Down
Loading

0 comments on commit cff465d

Please sign in to comment.