@nyaruka/temba-components 0.43.0 → 0.43.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/{96498fd6.js → 850b9c76.js} +434 -412
  3. package/dist/index.js +434 -412
  4. package/dist/sw.js +1 -1
  5. package/dist/sw.js.map +1 -1
  6. package/dist/templates/components-body.html +1 -1
  7. package/dist/templates/components-head.html +1 -1
  8. package/out-tsc/src/compose/Compose.js +47 -50
  9. package/out-tsc/src/compose/Compose.js.map +1 -1
  10. package/out-tsc/src/contacts/ContactChat.js +2 -2
  11. package/out-tsc/src/contacts/ContactChat.js.map +1 -1
  12. package/out-tsc/src/dialog/Dialog.js +0 -2
  13. package/out-tsc/src/dialog/Dialog.js.map +1 -1
  14. package/out-tsc/src/dropdown/Dropdown.js +0 -1
  15. package/out-tsc/src/dropdown/Dropdown.js.map +1 -1
  16. package/out-tsc/src/lightbox/Lightbox.js +10 -7
  17. package/out-tsc/src/lightbox/Lightbox.js.map +1 -1
  18. package/out-tsc/src/list/ContentMenu.js +0 -4
  19. package/out-tsc/src/list/ContentMenu.js.map +1 -1
  20. package/out-tsc/src/list/TembaMenu.js +47 -18
  21. package/out-tsc/src/list/TembaMenu.js.map +1 -1
  22. package/out-tsc/src/utils/index.js +10 -1
  23. package/out-tsc/src/utils/index.js.map +1 -1
  24. package/out-tsc/test/temba-compose.test.js +5 -7
  25. package/out-tsc/test/temba-compose.test.js.map +1 -1
  26. package/out-tsc/test/temba-contact-chat.test.js +12 -12
  27. package/out-tsc/test/temba-contact-chat.test.js.map +1 -1
  28. package/package.json +1 -1
  29. package/screenshots/truth/menu/menu-focused-with items.png +0 -0
  30. package/screenshots/truth/menu/menu-refresh-1.png +0 -0
  31. package/screenshots/truth/menu/menu-refresh-2.png +0 -0
  32. package/screenshots/truth/menu/menu-root.png +0 -0
  33. package/screenshots/truth/menu/menu-submenu.png +0 -0
  34. package/screenshots/truth/menu/menu-tasks-nextup.png +0 -0
  35. package/screenshots/truth/menu/menu-tasks.png +0 -0
  36. package/src/compose/Compose.ts +54 -55
  37. package/src/contacts/ContactChat.ts +2 -2
  38. package/src/dialog/Dialog.ts +0 -2
  39. package/src/dropdown/Dropdown.ts +0 -1
  40. package/src/lightbox/Lightbox.ts +13 -9
  41. package/src/list/ContentMenu.ts +0 -4
  42. package/src/list/TembaMenu.ts +47 -18
  43. package/src/utils/index.ts +8 -1
  44. package/test/temba-compose.test.ts +5 -8
  45. package/test/temba-contact-chat.test.ts +12 -12
@@ -1 +1 @@
1
- {"version":3,"file":"temba-contact-chat.test.js","sourceRoot":"","sources":["../../test/temba-contact-chat.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { useFakeTimers } from 'sinon';\nimport { Button } from '../src/button/Button';\nimport { Compose } from '../src/compose/Compose';\nimport { ContactChat } from '../src/contacts/ContactChat';\nimport { CustomEventType } from '../src/interfaces';\nimport { TicketList } from '../src/list/TicketList';\nimport {\n assertScreenshot,\n clearMockPosts,\n getClip,\n getComponent,\n loadStore,\n mockGET,\n mockNow,\n mockPOST,\n} from '../test/utils.test';\nimport {\n getFailText,\n getSuccessFiles,\n getSuccessText,\n updateComponent,\n} from './temba-compose.test';\n\nlet clock: any;\nmockNow('2021-03-31T00:31:00.000-00:00');\n\nconst TAG = 'temba-contact-chat';\nconst getContactChat = async (attrs: any = {}) => {\n attrs['endpoint'] = '/test-assets/contacts/';\n // add some sizes and styles to force our chat history to scroll\n const chat = (await getComponent(\n TAG,\n attrs,\n '',\n 500,\n 500,\n 'display:flex;flex-direction:column;flex-grow:1;min-height:0;'\n )) as ContactChat;\n\n // TODO: this should be waiting for an event instead\n await waitFor(100);\n return chat;\n};\n\nconst list_TAG = 'temba-list';\nconst getTicketList = async (attrs: any = {}) => {\n const list = (await getComponent(list_TAG, attrs)) as TicketList;\n\n if (!list.endpoint) {\n return list;\n }\n\n return new Promise<TicketList>(resolve => {\n list.addEventListener(\n CustomEventType.FetchComplete,\n async () => {\n resolve(list);\n },\n { once: true }\n );\n });\n};\n\ndescribe('temba-contact-chat - contact tests', () => {\n // map requests for contact history to our static files\n // we'll just us the same historylist for everybody for now\n beforeEach(() => {\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('can be created', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n\n await assertScreenshot('contacts/contact-active-default', getClip(chat));\n });\n\n it('show history and show chatbox if contact is active', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n\n await assertScreenshot(\n 'contacts/contact-active-show-chatbox',\n getClip(chat)\n );\n });\n\n it('show history and hide chatbox if contact is archived', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-barack-archived',\n });\n\n await assertScreenshot(\n 'contacts/contact-archived-hide-chatbox',\n getClip(chat)\n );\n });\n\n it('show history and hide chatbox if contact is blocked', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-michelle-blocked',\n });\n\n await assertScreenshot(\n 'contacts/contact-blocked-hide-chatbox',\n getClip(chat)\n );\n });\n\n it('show history and hide chatbox if contact is stopped', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-tim-stopped',\n });\n\n await assertScreenshot(\n 'contacts/contact-stopped-hide-chatbox',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - contact tests - handle send tests - text no attachments', () => {\n beforeEach(() => {\n clearMockPosts();\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('with text no attachments - success', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getSuccessText());\n\n const response_body = {\n contacts: [{ uuid: 'contact-dave-active', name: 'Dave Matthews' }],\n text: { eng: 'sà-wàd-dee!' },\n attachments: { eng: [] },\n };\n mockPOST(/api\\/v2\\/broadcasts\\.json/, response_body);\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-no-attachments-success',\n getClip(chat)\n );\n });\n\n it('with text no attachments - failure - more than 640 chars', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the chatbox to a string that is 640+ chars\n await updateComponent(compose, getFailText());\n\n const response_body = {\n text: { eng: ['Ensure this field has no more than 640 characters.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/broadcasts\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-no-attachments-failure',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - contact tests - handle send tests - attachments no text', () => {\n beforeEach(() => {\n clearMockPosts();\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('with attachments no text - success', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const attachments = getSuccessFiles();\n await updateComponent(compose, null, attachments);\n const response_body = {\n contacts: [{ uuid: 'contact-dave-active', name: 'Dave Matthews' }],\n text: { eng: '' },\n attachments: { eng: attachments },\n };\n const response_headers = {};\n const response_status = '200';\n mockPOST(\n /api\\/v2\\/broadcasts\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-attachments-no-text-success',\n getClip(chat)\n );\n });\n it('with attachments no text - failure - more than 10 files', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the attachments to a list that is 10+ items\n await updateComponent(compose, null, getSuccessFiles(11));\n\n const response_body = {\n attachments: { eng: ['Ensure this field has no more than 10 elements.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/broadcasts\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-attachments-no-text-failure',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - contact tests - handle send tests - text and attachments', () => {\n beforeEach(() => {\n clearMockPosts();\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('with text and attachments - success', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getSuccessText(), getSuccessFiles());\n\n const text = getSuccessText();\n const attachments = getSuccessFiles();\n const response_body = {\n contacts: [{ uuid: 'contact-dave-active', name: 'Dave Matthews' }],\n text: { eng: text },\n attachments: { eng: attachments },\n };\n mockPOST(/api\\/v2\\/broadcasts\\.json/, response_body);\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-success',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - more than 640 chars', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the chatbox to a string that is 640+ chars\n await updateComponent(compose, getFailText(), getSuccessFiles());\n\n const response_body = {\n text: { eng: ['Ensure this field has no more than 640 characters.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/broadcasts\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-text',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - more than 10 files', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the attachments to a list that is 10+ items\n await updateComponent(compose, getSuccessText(), getSuccessFiles(11));\n\n const response_body = {\n attachments: { eng: ['Ensure this field has no more than 10 elements.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/broadcasts\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-attachments',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - more than 640 chars and more than 10 files', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the chatbox to a string that is 640+ chars\n // set the attachments to a list that is 10+ items\n await updateComponent(compose, getFailText(), getSuccessFiles(11));\n\n const response_body = {\n text: { eng: ['Ensure this field has no more than 640 characters.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/broadcasts\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-text-and-attachments',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - generic', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getSuccessText(), getSuccessFiles());\n\n const response_body = {};\n const response_headers = {};\n const response_status = '500';\n mockPOST(\n /api\\/v2\\/broadcasts\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-generic',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - ticket tests', () => {\n // map requests for contact history to our static files\n // we'll just us the same history for everybody for now\n beforeEach(() => {\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n\n mockGET(/\\/api\\/v2\\/tickets\\.json.*/, '/test-assets/tickets/empty.json');\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('show history and show chatbox if contact is active and ticket is open', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-carter-active',\n });\n\n const tickets: TicketList = await getTicketList({\n endpoint: '/test-assets/tickets/ticket-carter-open.json',\n });\n\n chat.currentTicket = tickets.items[0];\n chat.refresh();\n await chat.httpComplete;\n\n // we want to wait until our scroll is finished before taking our screenshot\n // once we have two scrollTops that are the same, we'll assume we're ready\n let lastScroll = 0;\n await assertScreenshot(\n 'contacts/contact-active-ticket-open-show-chatbox',\n getClip(chat),\n {\n clock: clock,\n predicate: () => {\n const currentScroll = chat\n .getContactHistory()\n .getEventsPane().scrollTop;\n if (currentScroll !== 0 && currentScroll === lastScroll) {\n return true;\n }\n lastScroll = currentScroll;\n },\n }\n );\n });\n\n it('show history and show reopen button if contact is active and ticket is closed', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-carter-active',\n });\n\n const tickets: TicketList = await getTicketList({\n endpoint: '/test-assets/tickets/ticket-carter-closed.json',\n });\n chat.currentTicket = tickets.items[0];\n chat.refresh();\n await chat.httpComplete;\n\n let lastScroll = 0;\n await assertScreenshot(\n 'contacts/contact-active-ticket-closed-show-reopen-button',\n getClip(chat),\n {\n clock: clock,\n predicate: () => {\n const currentScroll = chat\n .getContactHistory()\n .getEventsPane().scrollTop;\n if (currentScroll !== 0 && currentScroll === lastScroll) {\n return true;\n }\n lastScroll = currentScroll;\n },\n }\n );\n });\n\n it('show history and hide chatbox if contact is archived and ticket is closed', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n\n const chat: ContactChat = await getContactChat({\n contact: 'contact-barack-archived',\n });\n\n const tickets: TicketList = await getTicketList({\n endpoint: '/test-assets/tickets/ticket-barack-closed.json',\n });\n chat.currentTicket = tickets.items[0];\n chat.refresh();\n await chat.httpComplete;\n let lastScroll = 0;\n await assertScreenshot(\n 'contacts/contact-archived-ticket-closed-hide-chatbox',\n getClip(chat),\n {\n clock: clock,\n predicate: () => {\n const currentScroll = chat\n .getContactHistory()\n .getEventsPane().scrollTop;\n if (currentScroll !== 0 && currentScroll === lastScroll) {\n return true;\n }\n lastScroll = currentScroll;\n },\n }\n );\n });\n});\n"]}
1
+ {"version":3,"file":"temba-contact-chat.test.js","sourceRoot":"","sources":["../../test/temba-contact-chat.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { useFakeTimers } from 'sinon';\nimport { Button } from '../src/button/Button';\nimport { Compose } from '../src/compose/Compose';\nimport { ContactChat } from '../src/contacts/ContactChat';\nimport { CustomEventType } from '../src/interfaces';\nimport { TicketList } from '../src/list/TicketList';\nimport {\n assertScreenshot,\n clearMockPosts,\n getClip,\n getComponent,\n loadStore,\n mockGET,\n mockNow,\n mockPOST,\n} from '../test/utils.test';\nimport {\n getFailText,\n getSuccessFiles,\n getSuccessText,\n updateComponent,\n} from './temba-compose.test';\n\nlet clock: any;\nmockNow('2021-03-31T00:31:00.000-00:00');\n\nconst TAG = 'temba-contact-chat';\nconst getContactChat = async (attrs: any = {}) => {\n attrs['endpoint'] = '/test-assets/contacts/';\n // add some sizes and styles to force our chat history to scroll\n const chat = (await getComponent(\n TAG,\n attrs,\n '',\n 500,\n 500,\n 'display:flex;flex-direction:column;flex-grow:1;min-height:0;'\n )) as ContactChat;\n\n // TODO: this should be waiting for an event instead\n await waitFor(100);\n return chat;\n};\n\nconst list_TAG = 'temba-list';\nconst getTicketList = async (attrs: any = {}) => {\n const list = (await getComponent(list_TAG, attrs)) as TicketList;\n\n if (!list.endpoint) {\n return list;\n }\n\n return new Promise<TicketList>(resolve => {\n list.addEventListener(\n CustomEventType.FetchComplete,\n async () => {\n resolve(list);\n },\n { once: true }\n );\n });\n};\n\ndescribe('temba-contact-chat - contact tests', () => {\n // map requests for contact history to our static files\n // we'll just us the same historylist for everybody for now\n beforeEach(() => {\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('can be created', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n\n await assertScreenshot('contacts/contact-active-default', getClip(chat));\n });\n\n it('show history and show chatbox if contact is active', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n\n await assertScreenshot(\n 'contacts/contact-active-show-chatbox',\n getClip(chat)\n );\n });\n\n it('show history and hide chatbox if contact is archived', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-barack-archived',\n });\n\n await assertScreenshot(\n 'contacts/contact-archived-hide-chatbox',\n getClip(chat)\n );\n });\n\n it('show history and hide chatbox if contact is blocked', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-michelle-blocked',\n });\n\n await assertScreenshot(\n 'contacts/contact-blocked-hide-chatbox',\n getClip(chat)\n );\n });\n\n it('show history and hide chatbox if contact is stopped', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-tim-stopped',\n });\n\n await assertScreenshot(\n 'contacts/contact-stopped-hide-chatbox',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - contact tests - handle send tests - text no attachments', () => {\n beforeEach(() => {\n clearMockPosts();\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('with text no attachments - success', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getSuccessText());\n\n const response_body = {\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n text: { eng: 'sà-wàd-dee!' },\n attachments: { eng: [] },\n };\n mockPOST(/api\\/v2\\/messages\\.json/, response_body);\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-no-attachments-success',\n getClip(chat)\n );\n });\n\n it('with text no attachments - failure - more than 640 chars', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the chatbox to a string that is 640+ chars\n await updateComponent(compose, getFailText());\n\n const response_body = {\n text: { eng: ['Ensure this field has no more than 640 characters.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-no-attachments-failure',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - contact tests - handle send tests - attachments no text', () => {\n beforeEach(() => {\n clearMockPosts();\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('with attachments no text - success', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n const attachments = getSuccessFiles();\n await updateComponent(compose, null, attachments);\n const response_body = {\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n text: { eng: '' },\n attachments: { eng: attachments },\n };\n const response_headers = {};\n const response_status = '200';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-attachments-no-text-success',\n getClip(chat)\n );\n });\n it('with attachments no text - failure - more than 10 files', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the attachments to a list that is 10+ items\n await updateComponent(compose, null, getSuccessFiles(11));\n\n const response_body = {\n attachments: { eng: ['Ensure this field has no more than 10 elements.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-attachments-no-text-failure',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - contact tests - handle send tests - text and attachments', () => {\n beforeEach(() => {\n clearMockPosts();\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('with text and attachments - success', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getSuccessText(), getSuccessFiles());\n\n const text = getSuccessText();\n const attachments = getSuccessFiles();\n const response_body = {\n contact: { uuid: 'contact-dave-active', name: 'Dave Matthews' },\n text: { eng: text },\n attachments: { eng: attachments },\n };\n mockPOST(/api\\/v2\\/messages\\.json/, response_body);\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-success',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - more than 640 chars', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the chatbox to a string that is 640+ chars\n await updateComponent(compose, getFailText(), getSuccessFiles());\n\n const response_body = {\n text: { eng: ['Ensure this field has no more than 640 characters.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-text',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - more than 10 files', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the attachments to a list that is 10+ items\n await updateComponent(compose, getSuccessText(), getSuccessFiles(11));\n\n const response_body = {\n attachments: { eng: ['Ensure this field has no more than 10 elements.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-attachments',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - more than 640 chars and more than 10 files', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n // set the chatbox to a string that is 640+ chars\n // set the attachments to a list that is 10+ items\n await updateComponent(compose, getFailText(), getSuccessFiles(11));\n\n const response_body = {\n text: { eng: ['Ensure this field has no more than 640 characters.'] },\n };\n const response_headers = {};\n const response_status = '400';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-text-and-attachments',\n getClip(chat)\n );\n });\n\n it('with text and attachments - failure - generic', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-dave-active',\n });\n const compose = chat.shadowRoot.querySelector('temba-compose') as Compose;\n await updateComponent(compose, getSuccessText(), getSuccessFiles());\n\n const response_body = {};\n const response_headers = {};\n const response_status = '500';\n mockPOST(\n /api\\/v2\\/messages\\.json/,\n response_body,\n response_headers,\n response_status\n );\n\n const send = compose.shadowRoot.querySelector(\n 'temba-button#send-button'\n ) as Button;\n send.click();\n\n await assertScreenshot(\n 'contacts/compose-text-and-attachments-failure-generic',\n getClip(chat)\n );\n });\n});\n\ndescribe('temba-contact-chat - ticket tests', () => {\n // map requests for contact history to our static files\n // we'll just us the same history for everybody for now\n beforeEach(() => {\n mockGET(\n /\\/contact\\/history\\/contact-.*/,\n '/test-assets/contacts/history.json'\n );\n\n mockGET(/\\/api\\/v2\\/tickets\\.json.*/, '/test-assets/tickets/empty.json');\n clock = useFakeTimers();\n });\n\n afterEach(function () {\n clock.restore();\n });\n\n it('show history and show chatbox if contact is active and ticket is open', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-carter-active',\n });\n\n const tickets: TicketList = await getTicketList({\n endpoint: '/test-assets/tickets/ticket-carter-open.json',\n });\n\n chat.currentTicket = tickets.items[0];\n chat.refresh();\n await chat.httpComplete;\n\n // we want to wait until our scroll is finished before taking our screenshot\n // once we have two scrollTops that are the same, we'll assume we're ready\n let lastScroll = 0;\n await assertScreenshot(\n 'contacts/contact-active-ticket-open-show-chatbox',\n getClip(chat),\n {\n clock: clock,\n predicate: () => {\n const currentScroll = chat\n .getContactHistory()\n .getEventsPane().scrollTop;\n if (currentScroll !== 0 && currentScroll === lastScroll) {\n return true;\n }\n lastScroll = currentScroll;\n },\n }\n );\n });\n\n it('show history and show reopen button if contact is active and ticket is closed', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n const chat: ContactChat = await getContactChat({\n contact: 'contact-carter-active',\n });\n\n const tickets: TicketList = await getTicketList({\n endpoint: '/test-assets/tickets/ticket-carter-closed.json',\n });\n chat.currentTicket = tickets.items[0];\n chat.refresh();\n await chat.httpComplete;\n\n let lastScroll = 0;\n await assertScreenshot(\n 'contacts/contact-active-ticket-closed-show-reopen-button',\n getClip(chat),\n {\n clock: clock,\n predicate: () => {\n const currentScroll = chat\n .getContactHistory()\n .getEventsPane().scrollTop;\n if (currentScroll !== 0 && currentScroll === lastScroll) {\n return true;\n }\n lastScroll = currentScroll;\n },\n }\n );\n });\n\n it('show history and hide chatbox if contact is archived and ticket is closed', async () => {\n // we are a StoreElement, so load a store first\n await loadStore();\n\n const chat: ContactChat = await getContactChat({\n contact: 'contact-barack-archived',\n });\n\n const tickets: TicketList = await getTicketList({\n endpoint: '/test-assets/tickets/ticket-barack-closed.json',\n });\n chat.currentTicket = tickets.items[0];\n chat.refresh();\n await chat.httpComplete;\n let lastScroll = 0;\n await assertScreenshot(\n 'contacts/contact-archived-ticket-closed-hide-chatbox',\n getClip(chat),\n {\n clock: clock,\n predicate: () => {\n const currentScroll = chat\n .getContactHistory()\n .getEventsPane().scrollTop;\n if (currentScroll !== 0 && currentScroll === lastScroll) {\n return true;\n }\n lastScroll = currentScroll;\n },\n }\n );\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nyaruka/temba-components",
3
- "version": "0.43.0",
3
+ "version": "0.43.2",
4
4
  "description": "Web components to support rapidpro and related projects",
5
5
  "author": "Nyaruka <code@nyaruka.coim>",
6
6
  "main": "dist/index.js",
@@ -12,17 +12,20 @@ import {
12
12
  WebResponse,
13
13
  } from '../utils';
14
14
  import { Completion } from '../completion/Completion';
15
+ import { VectorIcon } from '../vectoricon/VectorIcon';
16
+ import { Button } from '../button/Button';
15
17
 
16
18
  export interface Attachment {
17
19
  uuid: string;
18
20
  content_type: string;
19
- type: string; //deprecated
20
21
  url: string;
21
- name: string;
22
+ filename: string;
22
23
  size: number;
23
24
  error: string;
24
25
  }
25
26
 
27
+ export const upload_endpoint = '/api/v2/media.json';
28
+
26
29
  export class Compose extends FormElement {
27
30
  static get styles() {
28
31
  return css`
@@ -199,7 +202,7 @@ export class Compose extends FormElement {
199
202
  accept = ''; //e.g. ".xls,.xlsx"
200
203
 
201
204
  @property({ type: String, attribute: false })
202
- endpoint = '/msgmedia/upload/';
205
+ endpoint = upload_endpoint;
203
206
 
204
207
  @property({ type: Boolean, attribute: false })
205
208
  uploading: boolean;
@@ -225,23 +228,27 @@ export class Compose extends FormElement {
225
228
  public updated(changes: Map<string, any>): void {
226
229
  super.updated(changes);
227
230
 
228
- if (
229
- changes.has('currentChat') ||
230
- changes.has('values') ||
231
- changes.has('buttonError')
232
- ) {
231
+ if (changes.has('currentChat') || changes.has('values')) {
232
+ this.buttonError = '';
233
233
  this.toggleButton();
234
234
  }
235
235
  }
236
236
 
237
237
  firstUpdated(): void {
238
- const completion = this.shadowRoot.querySelector(
239
- 'temba-completion'
240
- ) as Completion;
241
- if (completion) {
242
- window.setTimeout(() => {
243
- completion.click();
244
- }, 0);
238
+ this.setFocusOnChatbox();
239
+ }
240
+
241
+ setFocusOnChatbox(): void {
242
+ if (this.chatbox) {
243
+ const completion = this.shadowRoot.querySelector(
244
+ 'temba-completion'
245
+ ) as Completion;
246
+ if (completion) {
247
+ //simulate a click inside the completion to set focus
248
+ window.setTimeout(() => {
249
+ completion.click();
250
+ }, 0);
251
+ }
245
252
  }
246
253
  }
247
254
 
@@ -334,19 +341,21 @@ export class Compose extends FormElement {
334
341
  payload.append('file', file);
335
342
  postFormData(url, payload)
336
343
  .then((response: WebResponse) => {
337
- if (response.json.error) {
338
- this.addErrorValue(file, response.json.error);
339
- } else {
340
- const attachment = response.json as Attachment;
341
- if (attachment) {
342
- this.addValue(attachment);
343
- this.fireCustomEvent(CustomEventType.AttachmentAdded, attachment);
344
- }
344
+ const attachment = response.json as Attachment;
345
+ if (attachment) {
346
+ this.addValue(attachment);
347
+ this.fireCustomEvent(CustomEventType.AttachmentAdded, attachment);
345
348
  }
346
349
  })
347
- .catch((error: string) => {
348
- console.log(error);
349
- this.addErrorValue(file, error);
350
+ .catch((error: WebResponse) => {
351
+ let fileError = '';
352
+ if (error.status === 400) {
353
+ fileError = error.json.file[0];
354
+ } else {
355
+ fileError = 'Server failure';
356
+ }
357
+ console.error(fileError);
358
+ this.addErrorValue(file, fileError);
350
359
  })
351
360
  .finally(() => {
352
361
  this.uploading = false;
@@ -357,12 +366,11 @@ export class Compose extends FormElement {
357
366
  const errorValue = {
358
367
  uuid: Math.random().toString(36).slice(2, 6),
359
368
  content_type: file.type,
360
- type: file.type,
361
- name: file.name,
369
+ filename: file.name,
362
370
  url: file.name,
363
371
  size: file.size,
364
372
  error: error,
365
- };
373
+ } as Attachment;
366
374
  this.errorValues.push(errorValue);
367
375
  this.requestUpdate('errorValues');
368
376
  }
@@ -392,20 +400,16 @@ export class Compose extends FormElement {
392
400
 
393
401
  public toggleButton() {
394
402
  if (this.button) {
395
- if (this.buttonError && this.buttonError.length > 0) {
396
- this.buttonDisabled = true;
403
+ const chatboxEmpty = this.currentChat.trim().length === 0;
404
+ const attachmentsEmpty = this.values.length === 0;
405
+ if (this.chatbox && this.attachments) {
406
+ this.buttonDisabled = chatboxEmpty && attachmentsEmpty;
407
+ } else if (this.chatbox) {
408
+ this.buttonDisabled = chatboxEmpty;
409
+ } else if (this.attachments) {
410
+ this.buttonDisabled = attachmentsEmpty;
397
411
  } else {
398
- const chatboxEmpty = this.currentChat.trim().length === 0;
399
- const attachmentsEmpty = this.values.length === 0;
400
- if (this.chatbox && this.attachments) {
401
- this.buttonDisabled = chatboxEmpty && attachmentsEmpty;
402
- } else if (this.chatbox) {
403
- this.buttonDisabled = chatboxEmpty;
404
- } else if (this.attachments) {
405
- this.buttonDisabled = attachmentsEmpty;
406
- } else {
407
- this.buttonDisabled = true;
408
- }
412
+ this.buttonDisabled = true;
409
413
  }
410
414
  }
411
415
  }
@@ -429,12 +433,9 @@ export class Compose extends FormElement {
429
433
  this.buttonDisabled = true;
430
434
  const name = this.buttonName;
431
435
  this.fireCustomEvent(CustomEventType.ButtonClicked, { name });
432
- }
433
- }
434
436
 
435
- private handleSendBlur() {
436
- if (this.buttonError.length > 0) {
437
- this.buttonError = '';
437
+ //after send, return focus to chatbox
438
+ this.setFocusOnChatbox();
438
439
  }
439
440
  }
440
441
 
@@ -469,7 +470,6 @@ export class Compose extends FormElement {
469
470
  @change=${this.handleChatboxChange}
470
471
  @keydown=${this.handleSendEnter}
471
472
  placeholder="Write something here"
472
- @blur=${this.handleSendBlur}
473
473
  >
474
474
  </temba-completion>`;
475
475
  }
@@ -492,13 +492,13 @@ export class Compose extends FormElement {
492
492
  </div>
493
493
  <div class="attachment-name">
494
494
  <span
495
- title="${attachment.name} (${formatFileSize(
495
+ title="${attachment.filename} (${formatFileSize(
496
496
  attachment.size,
497
497
  2
498
- )}) ${attachment.type}"
499
- >${truncate(attachment.name, 25)}
498
+ )}) ${attachment.content_type}"
499
+ >${truncate(attachment.filename, 25)}
500
500
  (${formatFileSize(attachment.size, 0)})
501
- ${formatFileType(attachment.type)}</span
501
+ ${formatFileType(attachment.content_type)}</span
502
502
  >
503
503
  </div>
504
504
  </div>`;
@@ -516,11 +516,11 @@ export class Compose extends FormElement {
516
516
  </div>
517
517
  <div class="attachment-name">
518
518
  <span
519
- title="${errorAttachment.name} (${formatFileSize(
519
+ title="${errorAttachment.filename} (${formatFileSize(
520
520
  0,
521
521
  0
522
522
  )}) - Attachment failed - ${errorAttachment.error}"
523
- >${truncate(errorAttachment.name, 25)}
523
+ >${truncate(errorAttachment.filename, 25)}
524
524
  (${formatFileSize(0, 0)}) - Attachment failed</span
525
525
  >
526
526
  </div>
@@ -579,7 +579,6 @@ export class Compose extends FormElement {
579
579
  name=${this.buttonName}
580
580
  @click=${this.handleSendClick}
581
581
  ?disabled=${this.buttonDisabled}
582
- @blur=${this.handleSendBlur}
583
582
  ></temba-button>`;
584
583
  }
585
584
  }
@@ -267,7 +267,7 @@ export class ContactChat extends ContactStoreElement {
267
267
  const buttonName = evt.detail.name;
268
268
  if (buttonName === 'Send') {
269
269
  const payload = {
270
- contacts: [this.currentContact.uuid],
270
+ contact: this.currentContact.uuid,
271
271
  };
272
272
  const compose = evt.currentTarget as Compose;
273
273
  if (compose) {
@@ -289,7 +289,7 @@ export class ContactChat extends ContactStoreElement {
289
289
 
290
290
  const genericError = buttonName + ' failed, please try again.';
291
291
 
292
- postJSON(`/api/v2/broadcasts.json`, payload)
292
+ postJSON(`/api/v2/messages.json`, payload)
293
293
  .then(response => {
294
294
  if (response.status < 400) {
295
295
  compose.reset();
@@ -215,14 +215,12 @@ export class Dialog extends RapidElement {
215
215
  this.animationEnd = false;
216
216
  }, 400);
217
217
 
218
- const scrollbarWidth = window.outerWidth - body.clientWidth;
219
218
  this.scrollOffset = -document.documentElement.scrollTop;
220
219
  body.style.position = 'fixed';
221
220
  body.style.overflowY = 'scroll';
222
221
  body.style.top = this.scrollOffset + 'px';
223
222
  body.style.width = '100%';
224
223
  body.style.overflowY = 'hidden';
225
- body.style.paddingRight = scrollbarWidth + 'px';
226
224
  } else {
227
225
  body.style.position = '';
228
226
  body.style.overflowY = '';
@@ -65,7 +65,6 @@ export class Dropdown extends RapidElement {
65
65
  background: rgba(0, 0, 0, 0.7);
66
66
  opacity: 0;
67
67
  transition: opacity var(--transition-speed) ease-in-out;
68
- z-index: 1;
69
68
  pointer-events: none;
70
69
  }
71
70
 
@@ -93,18 +93,20 @@ export class Lightbox extends RapidElement {
93
93
 
94
94
  let desiredWidth = this.width;
95
95
  let desiredHeight = this.height;
96
+ let desiredScale = this.scale;
96
97
 
97
- // set our destination and size
98
- if (this.height > this.width) {
98
+ const maxHeight = window.innerHeight * this.zoomPct;
99
+ const maxWidth = window.innerWidth * this.zoomPct;
100
+
101
+ // if the width fits, constrain by height
102
+ if (this.width * (maxHeight / this.height) < maxWidth) {
99
103
  desiredHeight = window.innerHeight * this.zoomPct;
100
- this.scale = desiredHeight / this.height;
101
- desiredWidth = this.width * this.scale;
102
- }
103
- // landscape
104
- else {
104
+ desiredScale = desiredHeight / this.height;
105
+ desiredWidth = this.width * desiredScale;
106
+ } else {
105
107
  desiredWidth = window.innerWidth * this.zoomPct;
106
- this.scale = desiredWidth / this.width;
107
- desiredHeight = this.height * this.scale;
108
+ desiredScale = desiredWidth / this.width;
109
+ desiredHeight = this.height * desiredScale;
108
110
  }
109
111
 
110
112
  const xGrowth = (desiredWidth - this.width) / 2;
@@ -114,6 +116,8 @@ export class Lightbox extends RapidElement {
114
116
  const yGrowth = (desiredHeight - this.height) / 2;
115
117
  const yDest = (window.innerHeight - desiredHeight) / 2;
116
118
  this.yTrans = yDest - this.top + yGrowth + 'px';
119
+
120
+ this.scale = desiredScale;
117
121
  this.show = true;
118
122
  }
119
123
 
@@ -80,10 +80,6 @@ export class ContentMenu extends RapidElement {
80
80
  .item:hover {
81
81
  color: rgb(var(--primary-rgb));
82
82
  }
83
-
84
- temba-dropdown {
85
- z-index: 20;
86
- }
87
83
  `;
88
84
  }
89
85
 
@@ -124,17 +124,39 @@ export class TembaMenu extends RapidElement {
124
124
 
125
125
  .level-0 > .item,
126
126
  .level-0 > temba-dropdown > div[slot='toggle'] > .avatar {
127
- background: var(--color-primary-dark);
127
+ padding: 0px;
128
128
  --icon-color: rgba(255, 255, 255, 0.7);
129
129
  font-size: 1em;
130
+ flex-direction: column;
131
+ border: 0px solid green;
132
+ width: 100%;
133
+ display: flex;
134
+ align-items: center;
130
135
  }
131
136
 
132
- .level-0 > .top {
133
- padding-top: var(--menu-padding);
137
+ .level-0 > .item.selected::before,
138
+ .level-0 > .item.selected::after {
139
+ content: ' ';
140
+ height: var(--curvature);
134
141
  background: var(--color-primary-dark);
135
- display: flex;
136
- flex-direction: column;
137
- align-items: center;
142
+ display: block;
143
+ width: 100%;
144
+ }
145
+
146
+ .level-0 > .item.selected::before {
147
+ border-bottom-right-radius: var(--curvature);
148
+ }
149
+
150
+ .level-0 > .item > temba-tip {
151
+ padding: 1em;
152
+ }
153
+
154
+ .level-0 > .item.selected::after {
155
+ border-top-right-radius: var(--curvature);
156
+ }
157
+
158
+ .level-0 {
159
+ padding-top: var(--menu-padding) !important;
138
160
  }
139
161
 
140
162
  .level-0 > .empty {
@@ -148,6 +170,14 @@ export class TembaMenu extends RapidElement {
148
170
  background: var(--color-primary-dark);
149
171
  }
150
172
 
173
+ .level-0 > temba-dropdown.open > div[slot='toggle'] > .avatar {
174
+ background: transparent !important;
175
+ }
176
+
177
+ .level-0 {
178
+ background: var(--color-primary-dark);
179
+ }
180
+
151
181
  temba-dropdown {
152
182
  z-index: 1;
153
183
  }
@@ -174,7 +204,7 @@ export class TembaMenu extends RapidElement {
174
204
  }
175
205
 
176
206
  .level-0 > .item.selected {
177
- background: inherit;
207
+ background: white;
178
208
  --icon-color: var(--color-primary-dark);
179
209
  }
180
210
 
@@ -184,7 +214,12 @@ export class TembaMenu extends RapidElement {
184
214
 
185
215
  .level-0 {
186
216
  padding: 0px;
187
- z-index: 500;
217
+ }
218
+
219
+ .top {
220
+ display: flex;
221
+ align-items: center;
222
+ flex-direction: column;
188
223
  }
189
224
 
190
225
  .item {
@@ -208,7 +243,6 @@ export class TembaMenu extends RapidElement {
208
243
  }
209
244
 
210
245
  .level-0 > .item {
211
- padding: 1em 1em;
212
246
  margin-top: 0em;
213
247
  border-radius: 0px;
214
248
  min-width: inherit;
@@ -249,12 +283,12 @@ export class TembaMenu extends RapidElement {
249
283
  }
250
284
 
251
285
  .level-0 > .item:hover {
252
- background: rgba(var(--primary-rgb), 0.85);
286
+ background: rgba(255, 255, 255, 0.15);
253
287
  --icon-color: #fff;
254
288
  }
255
289
 
256
290
  .level-0 > .item.selected:hover {
257
- background: inherit;
291
+ background: white;
258
292
  --icon-color: var(--color-primary-dark);
259
293
  cursor: default;
260
294
  }
@@ -272,13 +306,11 @@ export class TembaMenu extends RapidElement {
272
306
  .level-1 {
273
307
  transition: opacity 100ms linear, margin 200ms linear;
274
308
  overflow-y: scroll;
275
- z-index: 150;
276
309
  }
277
310
 
278
311
  .level-2 {
279
312
  background: #fbfbfb;
280
313
  overflow-y: auto;
281
- z-index: 1000;
282
314
  }
283
315
 
284
316
  .level-2 .item .details {
@@ -889,9 +921,7 @@ export class TembaMenu extends RapidElement {
889
921
  icon = renderAvatar({ name: menuItem.avatar });
890
922
  }
891
923
 
892
- const item = html` <div
893
- class="item-top ${isSelected ? 'selected' : null} "
894
- ></div>
924
+ const item = html`
895
925
  <div
896
926
  id="menu-${menuItem.id}"
897
927
  class="${itemClasses}"
@@ -946,8 +976,7 @@ export class TembaMenu extends RapidElement {
946
976
  </div>
947
977
  <div class="right"></div>
948
978
  </div>
949
-
950
- <div class="item-bottom ${isSelected ? 'selected' : null}"></div>`;
979
+ `;
951
980
 
952
981
  if (menuItem.popup) {
953
982
  return html`
@@ -1,6 +1,7 @@
1
1
  /* eslint-disable @typescript-eslint/no-this-alias */
2
2
  import { html, TemplateResult } from 'lit-html';
3
3
  import { Button } from '../button/Button';
4
+ import { upload_endpoint } from '../compose/Compose';
4
5
  import { Dialog } from '../dialog/Dialog';
5
6
  import { ContactField, Ticket, User } from '../interfaces';
6
7
  import ColorHash from 'color-hash';
@@ -241,10 +242,16 @@ export const postFormData = (
241
242
  .then(response => {
242
243
  if (response.status >= 200 && response.status < 300) {
243
244
  resolve(response);
245
+ } else {
246
+ if (url === upload_endpoint) {
247
+ reject(response);
248
+ } else {
249
+ reject('Server failure');
250
+ }
244
251
  }
245
- reject('Server failure');
246
252
  })
247
253
  .catch(err => {
254
+ console.error(err);
248
255
  reject(err);
249
256
  });
250
257
  });