Skip to content

Commit

Permalink
UI: [VAULT-21435] Message details (#24645)
Browse files Browse the repository at this point in the history
* WIP

* Fix timezone bug

* Fix date issues on create/edit form

* Add details screen

* Use allFields instead of formFields

* Fix tests

* Address comments!
  • Loading branch information
kiannaquach authored Jan 2, 2024
1 parent 7fb7a19 commit 1a69722
Show file tree
Hide file tree
Showing 8 changed files with 216 additions and 13 deletions.
27 changes: 23 additions & 4 deletions ui/app/serializers/config-ui/message.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,24 @@ export default class MessageSerializer extends ApplicationSerializer {
attrs = {
link: { serialize: false },
active: { serialize: false },
start_time: { serialize: false },
end_time: { serialize: false },
};

getISODateFormat(snapshotDateTime, jsonDateTime) {
if (typeof snapshotDateTime === 'object') {
return jsonDateTime;
}

// if the snapshot date is in local date time format ("yyyy-MM-dd'T'HH:mm"), we want to ensure
// it gets converted to an ISOString
if (typeof snapshotDateTime === 'string' && !snapshotDateTime.includes('Z')) {
return new Date(snapshotDateTime).toISOString();
}

return snapshotDateTime;
}

normalizeResponse(store, primaryModelClass, payload, id, requestType) {
if (requestType === 'queryRecord') {
const transformed = {
Expand All @@ -33,10 +49,13 @@ export default class MessageSerializer extends ApplicationSerializer {
title: json?.link_title || '',
href: json?.link_href || '',
};
// using the snapshot startTime and endTime since the json start and end times are null when
// it gets to the serialize function.
json.start_time = snapshot.record.startTime;
json.end_time = snapshot.record.endTime;
// When editing a message with pre-populated dates, this returns a date object. In this case, we would want to use
// the json date from the serializer. When selecting a date from the datetime-local input date picker, the dates gets
// set as a date time local string in the model - we would want to convert this local string to an ISOString. Lastly,
// if this date is not an object and isn’t a local date string, then return the snapshot date, which is set by default
// values defined on the model.
json.start_time = this.getISODateFormat(snapshot.record.startTime, json.start_time);
json.end_time = this.getISODateFormat(snapshot.record.endTime, json.end_time);
delete json?.link_title;
delete json?.link_href;
return json;
Expand Down
50 changes: 50 additions & 0 deletions ui/lib/config-ui/addon/components/messages/page/details.hbs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
{{!
Copyright (c) HashiCorp, Inc.
SPDX-License-Identifier: BUSL-1.1
~}}

<Messages::TabPageHeader
@authenticated={{@message.authenticated}}
@pageTitle={{@message.title}}
@breadcrumbs={{@breadcrumbs}}
/>

<Toolbar>
<ToolbarActions aria-label="message delete and edit">
{{#if @message.canDeleteCustomMessages}}
<ConfirmAction
class="toolbar-button"
@buttonColor="secondary"
@onConfirmAction={{this.deleteMessage}}
@confirmTitle="Are you sure?"
@confirmMessage="This will delete this message permanently. You cannot undo this action."
@buttonText="Delete message"
data-test-confirm-action="Delete message"
/>
<div class="toolbar-separator"></div>
{{/if}}
{{#if @message.canEditCustomMessages}}
<LinkTo class="toolbar-link" @route="messages.message.edit" @model={{@message.id}} data-test-link="edit">
Edit message
<Icon @name="chevron-right" />
</LinkTo>
{{/if}}
</ToolbarActions>
</Toolbar>

{{#each @message.allFields as |attr|}}
{{#if (or (eq attr.name "endTime") (eq attr.name "startTime"))}}
{{! if the attr is an endTime and is falsy, we want to show a 'Never' text value }}
<InfoTableRow
@label={{capitalize (humanize (dasherize attr.name))}}
@value={{if
(and (eq attr.name "endTime") (not (get @message attr.name)))
"Never"
(date-format (get @message attr.name) "MMM d, yyyy hh:mm aaa" withTimeZone=true)
}}
/>
{{else}}
<InfoTableRow @label={{capitalize (humanize (dasherize attr.name))}} @value={{get @message attr.name}} />
{{/if}}

{{/each}}
32 changes: 32 additions & 0 deletions ui/lib/config-ui/addon/components/messages/page/details.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import Component from '@glimmer/component';
import { inject as service } from '@ember/service';
import { action } from '@ember/object';

/**
* @module Page::MessageDetails
* Page::MessageDetails components are used to display a message
* @example
* ```js
* <Page::MessageDetails @message={{this.message}} />
* ```
* @param {model} message - message model
*/

export default class MessageDetails extends Component {
@service store;
@service router;
@service flashMessages;

@action
async deleteMessage() {
this.store.clearDataset('config-ui/message');
await this.args.message.destroyRecord(this.args.message.id);
this.router.transitionTo('vault.cluster.config-ui.messages');
this.flashMessages.success(`Successfully deleted ${this.args.message.title}.`);
}
}
1 change: 1 addition & 0 deletions ui/lib/config-ui/addon/components/messages/page/list.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
@route="messages.create"
@query={{hash authenticated=@authenticated}}
@type="add"
aria-label="message create"
>
Create message
</ToolbarLink>
Expand Down
20 changes: 19 additions & 1 deletion ui/lib/config-ui/addon/routes/messages/message/details.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,23 @@
*/

import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class MessagesMessageDetailsRoute extends Route {}
export default class MessagesMessageDetailsRoute extends Route {
@service store;

model() {
const { id } = this.paramsFor('messages.message');

return this.store.queryRecord('config-ui/message', id);
}

setupController(controller, resolvedModel) {
super.setupController(controller, resolvedModel);

controller.breadcrumbs = [
{ label: 'Messages', route: 'messages', query: { authenticated: resolvedModel.authenticated } },
{ label: resolvedModel.title },
];
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,4 @@
SPDX-License-Identifier: BUSL-1.1
~}}

Message Details
{{outlet}}
<Messages::Page::Details @message={{this.model}} @breadcrumbs={{this.breadcrumbs}} />
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
/**
* Copyright (c) HashiCorp, Inc.
* SPDX-License-Identifier: BUSL-1.1
*/

import { module, test } from 'qunit';
import { setupRenderingTest } from 'vault/tests/helpers';
import { setupMirage } from 'ember-cli-mirage/test-support';
import { setupEngine } from 'ember-engines/test-support';
import { render } from '@ember/test-helpers';
import { hbs } from 'ember-cli-htmlbars';
import { dateFormat } from 'core/helpers/date-format';

const allFields = [
{ label: 'Active', key: 'active' },
{ label: 'Type', key: 'type' },
{ label: 'Authenticated', key: 'authenticated' },
{ label: 'Title', key: 'title' },
{ label: 'Message', key: 'message' },
{ label: 'Start time', key: 'startTime' },
{ label: 'End time', key: 'endTime' },
];

module('Integration | Component | messages/page/details', function (hooks) {
setupRenderingTest(hooks);
setupEngine(hooks, 'config-ui');
setupMirage(hooks);

hooks.beforeEach(function () {
this.context = { owner: this.engine };
this.store = this.owner.lookup('service:store');

this.server.post('/sys/capabilities-self', () => ({
data: {
capabilities: ['root'],
},
}));

this.store.pushPayload('config-ui/message', {
modelName: 'config-ui/message',
id: '01234567-89ab-cdef-0123-456789abcdef',
active: true,
type: 'banner',
authenticated: true,
title: 'Message title 1',
message: 'Some long long long message',
link: { title: 'here', href: 'www.example.com' },
startTime: '2021-08-01T00:00:00Z',
endTime: '',
canDeleteCustomMessages: true,
canEditCustomMessages: true,
});
});

test('it should show the message details', async function (assert) {
this.message = await this.store.peekRecord('config-ui/message', '01234567-89ab-cdef-0123-456789abcdef');

await render(hbs`<Messages::Page::Details @message={{this.message}} />`, {
owner: this.engine,
});
assert.dom('[data-test-page-title]').hasText('Message title 1');
assert
.dom('[data-test-component="info-table-row"]')
.exists({ count: allFields.length }, 'Correct number of filtered fields render');
allFields.forEach((field) => {
assert
.dom(`[data-test-row-label="${field.label}"]`)
.hasText(field.label, `${field.label} label renders`);
if (field.key === 'startTime' || field.key === 'endTime') {
const formattedDate = dateFormat([this.message[field.key], 'MMM d, yyyy hh:mm aaa'], {
withTimeZone: true,
});
assert
.dom(`[data-test-row-value="${field.label}"]`)
.hasText(formattedDate || 'Never', `${field.label} value renders`);
} else if (field.key === 'authenticated' || field.key === 'active') {
assert
.dom(`[data-test-value-div="${field.label}"]`)
.hasText(this.message[field.key] ? 'Yes' : 'No', `${field.label} value renders`);
} else {
assert
.dom(`[data-test-row-value="${field.label}"]`)
.hasText(this.message[field.key], `${field.label} value renders`);
}
});
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ import { hbs } from 'ember-cli-htmlbars';

const META = {
currentPage: 1,
lastPage: 2,
nextPage: 2,
lastPage: 1,
nextPage: 1,
prevPage: 1,
total: 16,
filteredTotal: 16,
total: 3,
pageSize: 15,
};

Expand Down Expand Up @@ -88,15 +87,13 @@ module('Integration | Component | messages/page/list', function (hooks) {
test('it should show the list of custom messages', async function (assert) {
this.messages = this.store.peekAll('config-ui/message', {});
this.messages.meta = META;

await render(hbs`<Messages::Page::List @messages={{this.messages}} />`, {
owner: this.engine,
});
assert.dom('[data-test-icon="message-circle"]').exists();
for (const message of this.messages) {
assert.dom(`[data-test-list-item="${message.id}"]`).exists();
assert.dom(`[data-linked-block-title="${message.id}"]`).hasText(message.title);
// TODO: add tests for active/inactive badges
}
});
});

0 comments on commit 1a69722

Please sign in to comment.